feat: 新增图片资源上传和管理服务
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
import {
|
||||
Controller,
|
||||
Post,
|
||||
Delete,
|
||||
Param,
|
||||
UseInterceptors,
|
||||
UploadedFile,
|
||||
Body,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
UseGuards,
|
||||
ParseFilePipe,
|
||||
MaxFileSizeValidator,
|
||||
FileTypeValidator,
|
||||
} from '@nestjs/common';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import {
|
||||
ApiTags,
|
||||
ApiOperation,
|
||||
ApiResponse,
|
||||
ApiConsumes,
|
||||
ApiBody,
|
||||
ApiBearerAuth,
|
||||
ApiParam,
|
||||
} from '@nestjs/swagger';
|
||||
import { StorageService } from './storage.service';
|
||||
import { UploadFileDto } from './dto/upload-file.dto';
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||
import { RolesGuard } from '../auth/guards/roles.guard';
|
||||
import { Roles } from '../auth/decorators/roles.decorator';
|
||||
import type { FileUpload } from './interfaces/storage-provider.interface';
|
||||
|
||||
@ApiTags('storage')
|
||||
@Controller('storage')
|
||||
export class StorageController {
|
||||
constructor(private readonly storageService: StorageService) {}
|
||||
|
||||
/**
|
||||
* 管理员上传文件(需要鉴权)
|
||||
* 用于上传券商Logo等基础数据
|
||||
*/
|
||||
@Post('upload')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@Roles('admin', 'super_admin')
|
||||
@ApiBearerAuth()
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
@ApiConsumes('multipart/form-data')
|
||||
@ApiOperation({
|
||||
summary: '管理员上传文件',
|
||||
description: '上传单个文件,需要管理员权限,用于上传券商Logo等基础数据',
|
||||
})
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
file: {
|
||||
type: 'string',
|
||||
format: 'binary',
|
||||
description: '要上传的文件',
|
||||
},
|
||||
folder: {
|
||||
type: 'string',
|
||||
enum: ['broker', 'user', 'temp'],
|
||||
description: '存储文件夹',
|
||||
},
|
||||
filename: {
|
||||
type: 'string',
|
||||
description: '自定义文件名',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: '上传成功',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: {
|
||||
type: 'string',
|
||||
example: 'broker/1234567890-abcdef-broker-logo.jpg',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
example:
|
||||
'http://localhost:3200/uploads/broker/1234567890-abcdef-broker-logo.jpg',
|
||||
},
|
||||
filename: {
|
||||
type: 'string',
|
||||
example: '1234567890-abcdef-broker-logo.jpg',
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
example: 102400,
|
||||
},
|
||||
mimetype: {
|
||||
type: 'string',
|
||||
example: 'image/jpeg',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiResponse({ status: 400, description: '文件格式或大小不符合要求' })
|
||||
@ApiResponse({ status: 401, description: '未授权' })
|
||||
@ApiResponse({ status: 403, description: '权限不足' })
|
||||
async uploadAdmin(
|
||||
@UploadedFile(
|
||||
new ParseFilePipe({
|
||||
validators: [
|
||||
new MaxFileSizeValidator({ maxSize: 5 * 1024 * 1024 }), // 5MB
|
||||
new FileTypeValidator({
|
||||
fileType: /^image\/(jpeg|jpg|png|gif|webp)$/,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
)
|
||||
file: FileUpload,
|
||||
@Body() uploadDto: UploadFileDto,
|
||||
) {
|
||||
const options = {
|
||||
folder: uploadDto.folder || 'temp',
|
||||
maxSize: 5 * 1024 * 1024, // 5MB
|
||||
allowedMimeTypes: [
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp',
|
||||
],
|
||||
filename: uploadDto.filename,
|
||||
};
|
||||
|
||||
return await this.storageService.upload(file, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户上传头像(不需要鉴权)
|
||||
* 用于用户注册或更新头像
|
||||
*/
|
||||
@Post('upload/avatar')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
@ApiConsumes('multipart/form-data')
|
||||
@ApiOperation({
|
||||
summary: '上传用户头像',
|
||||
description: '上传用户头像,不需要鉴权,用于用户注册或更新头像',
|
||||
})
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
file: {
|
||||
type: 'string',
|
||||
format: 'binary',
|
||||
description: '要上传的头像文件',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: '上传成功',
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: {
|
||||
type: 'string',
|
||||
example: 'user/1234567890-abcdef-avatar.jpg',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
example:
|
||||
'http://localhost:3200/uploads/user/1234567890-abcdef-avatar.jpg',
|
||||
},
|
||||
filename: {
|
||||
type: 'string',
|
||||
example: '1234567890-abcdef-avatar.jpg',
|
||||
},
|
||||
size: {
|
||||
type: 'number',
|
||||
example: 102400,
|
||||
},
|
||||
mimetype: {
|
||||
type: 'string',
|
||||
example: 'image/jpeg',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ApiResponse({ status: 400, description: '文件格式或大小不符合要求' })
|
||||
async uploadAvatar(
|
||||
@UploadedFile(
|
||||
new ParseFilePipe({
|
||||
validators: [
|
||||
new MaxFileSizeValidator({ maxSize: 2 * 1024 * 1024 }), // 2MB
|
||||
new FileTypeValidator({
|
||||
fileType: /^image\/(jpeg|jpg|png|gif|webp)$/,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
)
|
||||
file: FileUpload,
|
||||
) {
|
||||
const options = {
|
||||
folder: 'user', // 用户头像固定存储在 user 文件夹
|
||||
maxSize: 2 * 1024 * 1024, // 2MB(头像文件限制更小)
|
||||
allowedMimeTypes: [
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp',
|
||||
],
|
||||
};
|
||||
|
||||
return await this.storageService.upload(file, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件(需要管理员权限)
|
||||
*/
|
||||
@Delete(':path(*)')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@Roles('admin', 'super_admin')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({
|
||||
summary: '删除文件',
|
||||
description: '根据文件路径删除文件,需要管理员权限',
|
||||
})
|
||||
@ApiParam({
|
||||
name: 'path',
|
||||
description: '文件路径(相对路径)',
|
||||
example: 'broker/1234567890-abcdef-broker-logo.jpg',
|
||||
})
|
||||
@ApiResponse({ status: 204, description: '删除成功' })
|
||||
@ApiResponse({ status: 404, description: '文件不存在' })
|
||||
@ApiResponse({ status: 401, description: '未授权' })
|
||||
@ApiResponse({ status: 403, description: '权限不足' })
|
||||
async delete(@Param('path') filePath: string) {
|
||||
await this.storageService.delete(filePath);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user