处理文件上传,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,
});