Nest 文件上传

Nest 文件上传主要采用 @nestjs/platform-express 提供的 Interceptor 来拦截请求并从请求中提取出文件信息传递给 Controller,如果传入 dest 选项还能将文件保存到服务端指定目录中。

本质上platform-express里的拦截器是对 multer 库的一些封装,源码中会调用 multer 的方法来实现提取文件的功能。

从单个指定字段中获取 file

typescript
1  @Post('upload')
2  @UseInterceptors(
3    FileInterceptor('aaa', {
4      dest: 'uploads',
5    }),
6  )
7  uploadFile(@UploadedFile() file: Express.Multer.File, @Body() body) {
8    console.log('body', body);
9    console.log('file', file);
10  }

从单个指定字段中获取 files

typescript
1  @Post('upload')
2  @UseInterceptors(
3    FilesInterceptor('aaa', 3, {
4      dest: 'uploads',
5    }),
6  )
7  uploadFile(@UploadedFiles() files: Array<Express.Multer.File>, @Body() body) {
8    console.log('body', body);
9    console.log('files', files);
10  }

从多个指定字段中获取 files

typescript
1  @Post('upload')
2  @UseInterceptors(
3    FileFieldsInterceptor(
4      [
5        { name: 'aaa', maxCount: 2 },
6        { name: 'bbb', maxCount: 2 },
7      ],
8      {
9        dest: 'uploads',
10      },
11    ),
12  )
13  uploadFile(@UploadedFiles() files: Array<Express.Multer.File>, @Body() body) {
14    console.log('body', body);
15    console.log('files', files);
16  }

自动识别带 files 的字段

typescript
1  @Post('upload')
2  @UseInterceptors(
3    AnyFilesInterceptor({
4      dest: 'uploads',
5    }),
6  )
7  uploadFile(@UploadedFiles() files: Array<Express.Multer.File>, @Body() body) {
8    console.log('body', body);
9    console.log('files', files);
10  }

ParseFilePipe 校验文件

typescript
1  @Post('upload')
2  @UseInterceptors(
3    AnyFilesInterceptor({
4      dest: 'uploads',
5    }),
6  )
7  uploadFile(
8    @UploadedFiles(
9      new ParseFilePipe({
10        validators: [
11          new MaxFileSizeValidator({ maxSize: 1024 * 100 }),
12          new FileTypeValidator({ fileType: 'image/png' }),
13        ],
14      }),
15    )
16    files: Array<Express.Multer.File>,
17    @Body() body,
18  ) {
19    console.log('body', body);
20    console.log('files', files);
21  }

这里主要使用两个常见的 validator:

  • MaxFileSizeValidator 检验文件大小
  • FileTypeValidator 校验文件类型

自定义 validator

自定义 validator 需要继承自 FileValidator

typescript
1import { FileValidator } from '@nestjs/common'
2
3export class CustomFileValidator extends FileValidator {
4  constructor(options) {
5    super(options)
6  }
7
8  isValid(file: Express.Multer.File): boolean | Promise<boolean> {
9    console.log('maxSize', this.validationOptions.maxSize)
10
11    if (file.size > this.validationOptions.maxSize) {
12      return false
13    }
14    return true
15  }
16  buildErrorMessage(file: Express.Multer.File): string {
17    return `文件 ${file.originalname} 大小超出 10k`
18  }
19}

new ParseFilePipe的 validators 选项中使用:

typescript
1  @Post('upload')
2  @UseInterceptors(
3    AnyFilesInterceptor({
4      dest: 'uploads',
5    }),
6  )
7  uploadFile(
8    @UploadedFiles(
9      new ParseFilePipe({
10        validators: [new CustomFileValidator({ maxSize: 1024 * 10 })],
11      }),
12    )
13    files: Array<Express.Multer.File>,
14    @Body() body,
15  ) {
16    console.log('body', body);
17    console.log('files', files);
18  }

总结

Nest 的文件上传是基于 multer 实现的。

Nest 对 multer 做了一层封装,提供了 FileInterceptor、FilesInterceptor、FileFieldsInterceptor、AnyFilesInterceptor 等拦截器。

这些拦截器在会拦截请求,然后从中解析出文件。

@UploadedFile@UploadedFiles装饰器将解析后的文件传递给 handler。

在此过程中,还能使用 ParseFilePipe对文件做验证,内置了 MaxFileSizeValidator、FileTypeValidator 等 validator,也支持自定义实现。