在 Nestjs 应用程序中上传文件之前,我需要验证多部分表单。问题是,如果正文验证失败,我不希望上传文件。 这是我编写代码的方式。
// User controller method for create user with upload image
@Post()
@UseInterceptors(FileInterceptor('image'))
create(
@Body() userInput: CreateUserDto,
@UploadedFile(
new ParseFilePipe({
validators: [
// some validator here
]
})
) image: Express.Multer.File,
) {
return this.userService.create({ ...userInput, image: image.path });
}
尝试了很多方法来扭转这个问题,但没有达到任何解决方案
拦截器在管道之前运行,因此除非您在服务中自行管理,否则无法保存文件不会发生。但是,另一个选项可能是自定义异常过滤器,它unlink
是错误的文件,这样您就不必担心上传后的问题
import { isArray } from 'lodash';
import {
ExceptionFilter,
Catch,
ArgumentsHost,
BadRequestException,
} from '@nestjs/common';
import { Request, Response } from 'express';
import * as fs from 'fs';
@Catch(BadRequestException)
export class DeleteFileOnErrorFilter implements ExceptionFilter {
catch(exception: BadRequestException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
const getFiles = (files: Express.Multer.File[] | unknown | undefined) => {
if (!files) return [];
if (isArray(files)) return files;
return Object.values(files);
};
const filePaths = getFiles(request.files);
for (const file of filePaths) {
fs.unlink(file.path, (err) => {
if (err) {
console.error(err);
return err;
}
});
}
response.status(status).json(exception.getResponse());
}
}
保存文件.interceptor.ts
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { Observable, throwError, tap } from 'rxjs';
import { writeFile, existsSync, mkdirSync } from 'fs';
import { join } from 'path';
import { Request } from 'express';
@Injectable()
export class SaveFileInterceptor implements NestInterceptor {
saveFile(file: Express.Multer.File) {
const fileType = file.mimetype ? file.mimetype.split('/')[0] : 'others';
const destination = join(__dirname, `../../src/public/upload/${fileType}`);
if (!existsSync(destination)) {
mkdirSync(destination, { recursive: true });
}
writeFile(
`${destination}/${file.filename}`,
file.buffer,
err => {
if (err) {
console.log(err);
throwError(err);
}
}
)
}
generateFilePath(file: Express.Multer.File) {
const fileType = file.mimetype ? file.mimetype.split('/')[0] : 'others';
file.path = `/upload/${fileType}/${file.filename}`; // path served as static file
}
generateFileName(file: Express.Multer.File) {
file.filename = Date.now() + '-' + Math.round(Math.random() * 1E9);
}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const req: Request = context.switchToHttp().getRequest();
if (req.file) {
this.generateFileName(req.file);
this.generateFilePath(req.file);
}
if (req.files) {
if (Array.isArray(req.files)) {
req.files.forEach(file => {
this.generateFileName(file);
this.generateFilePath(file);
});
}
else {
Object.values(req.files).flat().forEach(file => {
this.generateFileName(file);
this.generateFilePath(file);
});
}
}
return next.handle().pipe(tap(() => {
if (req.file) {
this.saveFile(req.file);
}
if (req.files) {
if (Array.isArray(req.files)) {
req.files.forEach(file => this.saveFile(file));
}
else {
Object.values(req.files).flat().forEach(file => this.saveFile(file));
}
}
}));
}
}
控制器
import {
Controller,
Post,
Body,
UseInterceptors,
UploadedFile,
UploadedFiles,
ParseFilePipeBuilder,
HttpStatus,
} from '@nestjs/common';
import { Public } from '../auth/public.decorator';
import {
FileFieldsInterceptor,
FileInterceptor,
FilesInterceptor,
} from '@nestjs/platform-express';
import { FileTypeValidator, FileSizeValidator } from 'src/common';
import { SaveFileInterceptor } from 'src/interceptors/save-file.interceptor';
import { UploadOneFileDto } from './upload.dto';
@Public()
@Controller('upload')
export class UploadController {
@Post('/one-file')
@UseInterceptors(
FileInterceptor('file'),
SaveFileInterceptor
)
uploadOneFile(
@UploadedFile(
new ParseFilePipeBuilder()
.addMaxSizeValidator({ maxSize: 1024 * 1024 })
.addFileTypeValidator({ fileType: /.(jpg|jpeg|png)$/ })
.build({ errorHttpStatusCode: HttpStatus.BAD_REQUEST }),
) file: Express.Multer.File,
@Body() body: UploadOneFileDto
) {
console.log(file);
}
}