Skip to main content

文件上传

单文件上传

在 Nest 中,上传单个文件的功能由 FileInterceptor()拦截器和 @UploadFile() 装饰器共同实现。当 FileInterceptor 绑定到控制器方法时,它负责拦截请求中包含的文件,并将这些文件保存到指定的位置。而 @UploadFile() 装饰器用于从请求中提取已上传的文件。

@Post('/upload')
@UseInterceptors(FileInterceptor('file', { dest: './uploads' }))
uploadFile(@UploadedFile() file: Express.Multer.File, @Body() body) {
return {
message: '上传成功',
file: file.filename,
};
}

FileInterceptor@nestjs/platform-express 包中。

Express.Multer.File 是上传文件的类型,不过会报类型错误,需要引入相关的类型文件。

pnpm add @types/multer -D

保存路径

除了在拦截器上直接指定文件上传后的保存位置之外,Multer 中间件提供了文件存储引擎,通常在根模块进行配置。

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MulterModule } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname } from 'path';
import crypto from 'node:crypto';

@Module({
imports: [
MulterModule.register({
storage: diskStorage({
destination: './uploads',
filename: (req, file, cb) => {
const randomName = crypto.randomBytes(16).toString('hex');
const filename = `${randomName}${extname(file.originalname)}`;
cb(null, filename);
},
}),
}),
],
controllers: [AppController],
providers: [AppService],
exports: [
MulterModule
]
})
export class AppModule {}

多文件上传

多文件上传,使用 FilesInterceptor@UploadedFiles 实现。

@Post('/uploads')
@UseInterceptors(
FilesInterceptor('files', 3),
)
uploadFiles(
@UploadedFiles() files: Array<Express.Multer.File>,
) {
return {
message: '上传成功',
files,
};
}

此外,还可指定多字段:

@Post('/uploads')
@UseInterceptors(
FileFieldsInterceptor([
// 一个字段最大可上传三个文件
{ name: 'files1', maxCount: 3 },
{ name: 'files2', maxCount: 3 },
]),
)
uploadFiles(
@UploadedFiles()
files: { file1?: Express.Multer.File[]; file2?: Express.Multer.File[] },
) {
console.log(files);x
return {
message: '上传成功',
files,
};
}

任意字段上传

如果并不知道有哪些字段是文件类型,可以用 AnyFilesInterceptor 进行任意字段文件上传。

@Post('/anyUploads')
@UseInterceptors(
AnyFilesInterceptor({
// storage: ''
}),
)
uploadAnyFiles(
@UploadedFiles() files: Array<Express.Multer.File>,
) {
return {
message: '上传成功',
files,
};
}

上传限制

如果要对上传的文件做一些限制,比如文件大小、类型等,可通过管道进行处理。

import {
ArgumentMetadata,
HttpException,
HttpStatus,
Injectable,
PipeTransform,
} from '@nestjs/common';

@Injectable()
export class FileValidationPipe implements PipeTransform {
transform(value: Express.Multer.File, metadata: ArgumentMetadata) {
if (value.size > 10 * 1024) {
console.log(value);
throw new HttpException(
'文件上传大小不能超过10k',
HttpStatus.BAD_REQUEST,
);
}
return value;
}
}

在 Controller 中应用:

@Post('/upload')
@UseInterceptors(FileInterceptor('file'))
uploadFile(
@UploadedFile(FileValidationPipe) file: Express.Multer.File,
) {
return {
message: '上传成功',
file: file.filename,
};
}

文件大小、类型的校验这种逻辑太过常见,没有特殊要求可以直接使用 Nest 已经封装好的:

@Post('upload')
@UseInterceptors(FileInterceptor('file'))
uploadFile(
@UploadedFile(
new ParseFilePipe({
validators: [
// 校验文件大小
new MaxFileSizeValidator({ maxSize: 1024 * 1024 }),
// 校验文件类型
new FileTypeValidator({
fileType: 'image/png',
skipMagicNumbersValidation: true,
}),
// new MyFileValidator({})
],
exceptionFactory: (err) => {
console.log(err);
throw new HttpException(err, 400);
},
}),
)
file: Express.Multer.File,
) {
return {
message: '上传成功',
file: file.filename,
};
}

此外也可自定义 validator,只要继承 FileValidator 即可:

import { FileValidator } from '@nestjs/common';

export class MyFileValidator extends FileValidator {
constructor(options: Record<string, any>) {
super(options);
}

isValid(file: Express.Multer.File): boolean | Promise<boolean> {
if (file.size > 10 * 1024) {
return false;
} else if (file.mimetype !== 'image/png') {
return false;
}
return true;
}
buildErrorMessage(file: Express.Multer.File): string {
if (file.size > 10 * 1024) {
return `文件 ---${file.originalname}--- 大小超出 10k`;
} else if (file.mimetype !== 'image/jpeg') {
return `文件 ---${file.originalname}--- 类型不是 image/jpeg`;
}
}
}