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,
});
相关推荐
hhw19911211 分钟前
vue总结
前端·javascript·vue.js
学习2年半13 分钟前
汇丰eee2
前端·spring
代码续发14 分钟前
Vue进行前端开发流程
前端·vue.js
zpjing~.~18 分钟前
CSS &符号
前端·css
冴羽1 小时前
SvelteKit 最新中文文档教程(19)—— 最佳实践之身份认证
前端·javascript·svelte
拉不动的猪1 小时前
ES2024 新增的数组方法groupBy
前端·javascript·面试
huangkaihao1 小时前
单元测试 —— 用Vitest解锁前端可靠性
前端
archko1 小时前
telophoto源码查看记录
java·服务器·前端
倒霉男孩1 小时前
HTML5元素
前端·html·html5
柯南二号2 小时前
CSS 学习提升网站或者项目
前端·css