NestJS17-File upload

处理文件上传,Nest提供了基于express的multer包的内置模块。Multer处理multipart/form-data类型的数据,它主要通过HTTPPOST请求来上传文件。这个模块完全可以配置并且您可以调整它的行为来符合您的项目需求。

警告

Multer不能处理不支持multipart/form-data形式的数据 并且,这个包也不能和FastifyAdapter一起使用

为了更好的类型安全,让我们来安装Multer类型包

bash 复制代码
$ npm i -D @types/multer

当这个包安装后,我们现在可以使用Express.Multer.File类型(您可以通过这个方法来导入:import { Express } from 'express'

基本例子

上传单个文件,简单的将Fileintreceptor()拦截器绑定到路由句柄并且使用@UploadedFile()装饰器从request中取得file文件。

ts 复制代码
@Post('upload')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file: Express.Multer.File) {
  console.log(file);
}

注意

FileInterceptor()装饰器从@nestjs/platform-express包导入。@UploadedFile()装饰器从@nestjs/common包导入。

FileInterceptor()装饰器有2个参数

  • fieldName:提供包含文件的HTML表单中字段名称的字符串
  • options:MulterOptions类型的可选对象。和multer构造器中使用的是同一个对象(更多

警告

FileInterceptor()可能与Google Firebase或其他第三方云提供商不兼容。

文件检查

许多时候都能很有效的检查进来的文件元数据,比如文件大小或者文件的mime-type。您能够创建您自制的管道并且和有UploadedFile装饰器的参数绑定。下面的例子演示了如何通过管道验证文件的基本大小:

ts 复制代码
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';

@Injectable()
export class FileSizeValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    // "value"是一个包含文件属性和元数据的对象
    const oneKb = 1000;
    return value.size < oneKb;
  }
}

Nest提供了内置管道来处理共通用例并促进/规范新增。这个管道叫做ParseFilePipe,如下:

ts 复制代码
@Post('file')
uploadFileAndPassValidation(
  @Body() body: SampleDto,
  @UploadedFile(
    new ParseFilePipe({
      validators: [
        // ... Set of file validator instances here
      ]
    })
  )
  file: Express.Multer.File,
) {
  return {
    body,
    file: file.buffer.toString(),
  };
}

如您所见,它需要特定一个文件验证的数组将会被ParseFilePipe执行。我将会讨论验证的接口,但是值得注意的是这个管道还有2个附加可选参数:

参数 说明
errorHttpStatusCode HTTP状态码将会因为任何的验证失败而被抛出
exceptionFactory 一个工厂它将会收到异常消息并返回一个异常

现在,回到FileValidator接口。为了让管道结合验证,您需要既使用内置实现又要提供您自己定义的FileValidator。看下例子:

ts 复制代码
export abstract class FileValidator<TValidationOptions = Record<string, any>> {
  constructor(protected readonly validationOptions: TValidationOptions) {}

  /**
   * 指示根据构造函数中传递的选项,是否应将此文件视为有效文件。
   * @param file请求对象中的文件
   */
  abstract isValid(file?: any): boolean | Promise<boolean>;

  /**
   * 在验证失败时生成错误消息。
   * @param file请求对象中的文件
   */
  abstract buildErrorMessage(file: any): string;
}

注意

FileValdator接口通过它的isValid方法提供异步验证。要利用类型安全性,还可以将file参数为Express.Multer.File类型,以防您使用express(默认)作为驱动程序。

FileValidator是一个通常的类它访问文件对象并且根据服务端提供的参数验证它。Nest有2个内置FileValidator实现您能在您的项目中使用:

  • MaxFileSizeValidator - 验证是否取得文件的大小小于提供的值(用bytes比较)
  • FileTypeValidator - 验证是否文件的mime-type类型符合提供的值

警告

为了验证文件类型,FileTypeValidator类使用multer检测到的类型。默认情况下,multer从用户设备上的文件扩展名推断文件类型。然而,它不会检查实际的文件内容。由于文件可以被重命名为任意扩展名,如果您的应用程序需要更安全的解决方案,考虑使用自定义实现(比如检查文件的magic number)。

为了明白这些如何能够结合上文FileParsePipe一起使用,我们将使用上一个示例中呈现的代码片段的修改版本:

ts 复制代码
@UploadedFile(
  new ParseFilePipe({
    validators: [
      new MaxFileSizeValidator({ maxSize: 1000 }),
      new FileTypeValidator({ fileType: 'image/jpeg' }),
    ],
  }),
)
file: Express.Multer.File,

注意

如果验证器的数量大幅增加或它们的选项使文件变得混乱,您可以将这个数组定义在一个单独的文件中,然后将其作为一个名为fileValidators的命名常量导入到这里

最终,您可以使用特殊的ParseFilePipeBuilder类它允许您组合和构建您的验证器。通过如下所示的方式使用它,您可以避免手动实例化每个验证器,并直接传递它们的选项:

ts 复制代码
@UploadedFile(
  new ParseFilePipeBuilder()
    .addFileTypeValidator({
      fileType: 'jpeg',
    })
    .addMaxSizeValidator({
      maxSize: 1000
    })
    .build({
      errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY
    }),
)
file: Express.Multer.File,

文件数组

为了上传文件数组(验证单个项目名),使用FileInterceptor()装饰器。这个装饰器有2个参数。

  • uploadedFields:一个对象数组,每个对象需要一个字符串值的项目名的name属性,一个可选maxCount属性。
  • options:可选的MulterOptions对象。

当使用FileFieldsInterceptor(),从@UploadedFiles()装饰器的request中取得文件(files)。

ts 复制代码
@Post('upload')
@UseInterceptors(FileFieldsInterceptor([
  { name: 'avatar', maxCount: 1 },
  { name: 'background', maxCount: 1 },
]))
uploadFile(@UploadedFiles() files: { avatar?: Express.Multer.File[], background?: Express.Multer.File[] }) {
  console.log(files);
}

任何文件

要上传带有任意字段名键的所有字段,请使用AnyFilesInterceptor()装饰器。此装饰器可以接受一个可选的options对象,如上所述。

当使用AnyFilesInterceptor()时,使用@UploadedFiles()装饰器从request中提取文件。

ts 复制代码
@Post('upload')
@UseInterceptors(AnyFilesInterceptor())
uploadFile(@UploadedFiles() files: Array<Express.Multer.File>) {
  console.log(files);
}

没有文件

要接受multipart/form-data但不允许上传任何文件,请使用NoFilesInterceptor。这会将多部分数据设置为请求主体的属性。如果请求中包含文件,它们将引发一个BadRequestException异常。

ts 复制代码
@Post('upload')
@UseInterceptors(NoFilesInterceptor())
handleMultiPartData(@Body() body) {
  console.log(body)
}

默认参数

您可以按照上述说明在文件拦截器中指定multer选项。要设置默认选项,您可以在导入MulterModule时调用静态的register()方法,传递支持的选项。您可以使用列在此处的所有选项。

ts 复制代码
MulterModule.register({
  dest: './upload',
});

提示

MulterModule类是从@nestjs/platform-express包导出的

异步配置

当您需要异步设定MulterModules参数来代替静态的配置,使用registerAsync()方法。和大多数动态模块一样,Nest提供了几个技术来处理异步配置

一个技术就是使用工厂模式:

ts 复制代码
MulterModule.registerAsync({
  useFactory: () => ({
    dest: './upload',
  }),
});

和其他的factory providers一样,我们的工厂函数可以是异步的能够通过inject注入依赖

ts 复制代码
MulterModule.registerAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    dest: configService.get<string>('MULTER_DEST'),
  }),
  inject: [ConfigService],
});

或者,您可以使用类来代替工厂用于配置MulterModule,如下:

ts 复制代码
MulterModule.registerAsync({
  useClass: MulterConfigService,
});

上述结构在MulterModule内部实例化MulterConfigService,使用它来创建所需的选项对象。请注意,在此示例中,MulterConfigService必须实现MulterOptionsFactory接口,如下所示。MulterModule将在提供的类的实例化对象上调用createMulterOptions()方法。

ts 复制代码
@Injectable()
class MulterConfigService implements MulterOptionsFactory {
  createMulterOptions(): MulterModuleOptions {
    return {
      dest: './upload',
    };
  }
}

如果您想要重用现有的选项提供程序,而不是在MulterModule内部创建一个私有副本,可以使用useExisting语法。

ts 复制代码
MulterModule.registerAsync({
  imports: [ConfigModule],
  useExisting: ConfigService,
});
相关推荐
泰山小张只吃荷园5 分钟前
期末Python复习-输入输出
java·前端·spring boot·python·spring cloud·docker·容器
悦涵仙子40 分钟前
vueuse中的useTemplateRefsList
前端·javascript·vue.js
萧萧玉树40 分钟前
分布式在线评测系统
前端·c++·后端·负载均衡
haima951 小时前
ubuntu安装chrome无法打开问题
前端·chrome
放逐者-保持本心,方可放逐1 小时前
XSS 与 CSRF 记录
前端·xss·csrf·浏览器安全
徊忆羽菲1 小时前
利用HTML5和CSS来实现一个漂亮的表格样式
前端·css·html5
不爱说话郭德纲1 小时前
Stylus、Less 和 Sass 的使用与区别
前端·css·面试·less·sass·stylus
凄凄迷人2 小时前
如何调试 chrome 崩溃日志(MAC)
前端·chrome·macos·crash
蒙特网站2 小时前
网站布局编辑器前端开发:设计要点与关键考量
前端·javascript·学习·html
理想不理想v2 小时前
前端开发工程师需要学什么?
java·前端·vue.js·webpack·node.js