NestJs 使用上传功能

为了处理文件上传,NestJS 提供了一个基于 multer 中间件包的内置模块。Multer 处理以 multipart/form-data 格式发布的数据,该格式主要用于通过 HTTP POST 请求上传文件。该模块是完全可配置的,您可以根据您的应用程序要求调整其行为。

注意:
Multer 无法处理不支持的多部分格式(multipart/form-data)的数据。另请注意,此包与 FastifyAdapter 不兼容。

为了更好的类型安全,让我们安装 Multertypings 包来负责检查文件的类型:

npm i -D @types/multer

基本例子

要上传单个文件,只需将 FileInterceptor() 拦截器绑定到路由处理程序,并使用 @UploadedFile() 装饰器从请求中提取文件。我们首先实现一个简单的上传例子,我们只是把上传的文件打印出来看一下:

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

从上面的例子可以看到FileInterceptor() 装饰器有两个参数:

  • fileName:提供保存文件的 HTML 表单中的字段名称的字符串
  • options:MulterOptions 类型的可选对象。这与 multer 构造函数使用的对象相同。multer 的 options 参数使用可以查看这里

文件验证

通常,验证传入的文件元数据(例如文件大小或文件 MIME 类型)非常有用。为此,您可以创建自己的 Pipe 并将其绑定到用 UploadedFile 装饰器注释的参数。下面的示例演示了如何实现基本文件大小验证器管道:

import {
    
    
  PipeTransform,
  Injectable,
  ArgumentMetadata,
  BadRequestException,
} from "@nestjs/common";

@Injectable()
export class FileSizeValidationPipe implements PipeTransform {
    
    
  transform(value: any, metadata: ArgumentMetadata) {
    
    
    const oneKb = 1000;

    if (value.size < oneKb) {
    
    
      return true;
    } else {
    
    
      throw new BadRequestException("上传文件超过指定大小");
    }
  }
}

使用自定义管道的话,我们可以在对应的路由上添加管道就可以,具体实例如下:

@Post('upload')
@UseInterceptors(FileInterceptor('file'))
@UsePipes(new FileSizeValidationPipe())
uploadFile(@UploadedFile() file: Express.Multer.File) {
    
    
  console.log(file);
}

除了可以使用上述方法来上传文件外,NestJS 还提供了一个内置管道来处理常见用例并促进/标准化新用例的添加。该管道称为 ParseFilePipe,您可以按如下方式使用它:

@Post('/file')
uploadFileAndPassValidation(
  @UploadedFile(
    new ParseFilePipe({
    
    
      errorHttpStatusCode: 405,
      exceptionFactory: (error) => {
    
     return '文件上传失败' },
      validators: [
        // 此处的文件验证器实例集
      ]
    })
  )
  file: Express.Multer.File,
) {
    
    
  return {
    
    
    file: file.buffer.toString(),
  };
}

从上述的例子可以看到,ParseFilePipe主要是有三个参数,其中validators属性是必须的,而errorHttpStatusCodeexceptionFactory是可选的参数。这两个可选参数的说明如下:

  • errorHttpStatusCode:任何验证器失败时抛出的 HTTP 状态代码。默认为 400(错误请求)
  • exceptionFactory:接收错误消息并返回错误的工厂

在 NestJS 中,它由两个内置的 FileValidator,具体说明如下:

  • MaxFileSizeValidator- 检查给定文件的大小是否小于提供的值(以 测量 bytes)
  • FileTypeValidator- 检查给定文件的 mime 类型是否与给定值匹配

两个内置的 FileValidator 与 ParseFilePipe 管道结合使用的具体例子如下:

@Post('/file')
uploadFileAndPassValidation(
  @UploadedFile(
    new ParseFilePipe({
    
    
      errorHttpStatusCode: 405,
      exceptionFactory: (error) => {
    
     return '文件上传失败' },
      validators: [
        new MaxFileSizeValidator({
    
     maxSize: 1000 }),
        new FileTypeValidator({
    
     fileType: 'image/jpeg' }),
      ]
    })
  )
  file: Express.Multer.File,
) {
    
    
  return {
    
    
    file: file.buffer.toString(),
  };
}

最后,您也可以使用特殊的 ParseFilePipeBuilder 类来组合和构造验证器。通过如下所示使用它,您可以避免手动实例化每个验证器,而只需直接传递它们的选项:

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

通过上述的介绍,我们已经知道怎么对上传的文件进行校验,而在上述的描述中我们使用了 NestJS 内置的文件校验类,我们自己也可以编写文件校验类,我们只要继承FileValidator抽象类就可以。具体简单实例如下:

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

export class CustomFileValidatot extends FileValidator {
    
    
  isValid(file?: any): boolean | Promise<boolean> {
    
    
    throw new Error("Method not implemented.");
  }
  buildErrorMessage(file: any): string {
    
    
    throw new Error("Method not implemented.");
  }
}

注意:
为了验证文件类型,FileTypeValidator 类使用 multer 检测到的类型。默认情况下,multer 根据用户设备上的文件扩展名派生文件类型。但是,它不检查实际的文件内容。由于文件可以重命名为任意扩展名,因此如果您的应用程序需要更安全的解决方案,请考虑使用自定义实现。

文件数组

要上传文件数组,请使用 FilesInterceptor() 装饰器,这个装饰器主要有如下三个参数:

  • fieldName:提供保存文件的 HTML 表单中的字段名称的字符串,建议名称后面加一个"s"
  • maxCount:定义可接受的最大文件数的可选数字
  • options:MulterOptions 类型的可选对象。这与 multer 构造函数使用的对象相同。multer 的 options 参数使用可以查看这里

我们来实现一个限制 2 个文件上传的请求,具体代码如下:

@Post('/files')
@UseInterceptors(FilesInterceptor('files', 2))
uploadFiles(@UploadedFiles() files: Array<Express.Multer.File>) {
    
    
  console.log(files)
  return {
    
    }
}

多个文件

要上传多个文件(所有文件都具有不同的字段名称键),请使用 FileFieldsInterceptor() 装饰器。这个装饰器有两个参数:

  • uploadedFields:对象数组,其中每个对象指定一个必需的 name 属性,其中包含指定字段名称的字符串值和一个可选的 maxCount 属性
  • options:MulterOptions 类型的可选对象。这与 multer 构造函数使用的对象相同。multer 的 options 参数使用可以查看这里

具体的实例如下:

@Post('/files')
@UseInterceptors(FileFieldsInterceptor([
  {
    
     name: 'files', maxCount: 1 },
  {
    
     name: 'excels', maxCount: 1 },
]))
uploadFiles(@UploadedFiles() files: {
    
     files?: Express.Multer.File[], excels?: Express.Multer.File[] }) {
    
    
  console.log(files)
  return {
    
    }
}

上传时不明确fileName的处理方法

普通情况下,我们上传文件的时候都会明确fileName的名称,但是有时候业务需求可能导致fileName名称不明确,这样我们可以使用AnyFilesInterceptor()来实现这样的需求。

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

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

默认选项

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

MulterModule.register({
    
    
  dest: "./upload",
});

异步配置

当您需要异步而不是静态设置 MulterModule 选项时,请使用 registerAsync() 方法。与大多数动态模块一样,Nest 提供了多种处理异步配置的技术。

一种技术是使用工厂函数:

MulterModule.registerAsync({
    
    
  useFactory: () => ({
    
    
    dest: "./upload",
  }),
});

与其他工厂提供者一样,我们的工厂函数可以是异步的,并且可以通过注入注入依赖项。

MulterModule.registerAsync({
    
    
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    
    
    dest: configService.get<string>("MULTER_DEST"),
  }),
  inject: [ConfigService],
});

或者,您可以使用类而不是工厂来配置 MulterModule,如下所示:

MulterModule.registerAsync({
    
    
  useClass: MulterConfigService,
});

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

@Injectable()
class MulterConfigService implements MulterOptionsFactory {
    
    
  createMulterOptions(): MulterModuleOptions {
    
    
    return {
    
    
      dest: "./upload",
    };
  }
}

如果您想重用现有选项提供程序而不是在 MulterModule 内创建私有副本,请使用 useExisting 语法。

MulterModule.registerAsync({
    
    
  imports: [ConfigModule],
  useExisting: ConfigService,
});

猜你喜欢

转载自blog.csdn.net/qq_33003143/article/details/132328011