NestJS FileTypeValidator兼容XLSX但不兼容CSV,需同时支持两者
NestJS 配置 FileTypeValidator 同时支持 CSV 和 XLSX 文件
问题场景
使用 NestJS 的 FileTypeValidator 验证上传文件时,.xlsx 文件能正常通过验证,但 .csv 文件(MIME 为 text/csv)验证失败。当前验证代码如下:
@UploadedFile( new ParseFilePipe({ validators: [ new FileTypeValidator({ fileType: /^(text\/csv|application\/vnd\.openxmlformats-officedocument\.spreadsheetml\.sheet)$/, }), ], fileIsRequired: true, }), )
调试日志显示上传文件的 MIME 类型:
DEBUG : text/csv Untitled spreadsheet.csv DEBUG : application/vnd.openxmlformats-officedocument.spreadsheetml.sheet test.xlsx
问题原因
FileTypeValidator 底层依赖 file-type 库,该库通过文件内容的魔术字节识别 MIME 类型,而非仅依赖请求头或文件名。CSV 属于纯文本文件,没有特定的魔术字节,因此 file-type 会将其识别为 text/plain,而非请求头中的 text/csv,导致正则匹配失败。
解决方案
方案1:调整正则并结合扩展名验证
修改正则表达式兼容 text/plain,同时添加自定义验证器检查文件扩展名,避免误判其他纯文本文件:
import { FileValidator, ParseFilePipe, UploadedFile } from '@nestjs/common'; // 自定义扩展名验证器 class FileExtensionValidator extends FileValidator { constructor(private readonly allowedExtensions: string[]) { super({}); } isValid(file: Express.Multer.File): boolean { const fileExtension = file.originalname.split('.').pop().toLowerCase(); return this.allowedExtensions.includes(fileExtension); } buildErrorMessage(): string { return `文件扩展名必须是 ${this.allowedExtensions.join(', ')}`; } } // 使用验证器 @UploadedFile( new ParseFilePipe({ validators: [ new FileTypeValidator({ fileType: /^(text\/csv|text\/plain|application\/vnd\.openxmlformats-officedocument\.spreadsheetml\.sheet)$/, }), new FileExtensionValidator(['csv', 'xlsx']), ], fileIsRequired: true, }), )
方案2:自定义验证器同时校验 MIME 和扩展名
直接自定义验证器,同时检查请求头中的 MIME 类型和文件扩展名,摆脱对 FileTypeValidator 的依赖:
import { FileValidator, ParseFilePipe, UploadedFile } from '@nestjs/common'; class SpreadsheetFileValidator extends FileValidator { private readonly allowedMimes = [ 'text/csv', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ]; private readonly allowedExtensions = ['csv', 'xlsx']; isValid(file: Express.Multer.File): boolean { const isValidMime = this.allowedMimes.includes(file.mimetype); const fileExtension = file.originalname.split('.').pop().toLowerCase(); const isValidExtension = this.allowedExtensions.includes(fileExtension); return isValidMime && isValidExtension; } buildErrorMessage(): string { return `仅支持上传 CSV 和 XLSX 文件`; } } // 使用验证器 @UploadedFile( new ParseFilePipe({ validators: [new SpreadsheetFileValidator()], fileIsRequired: true, }), )
内容的提问来源于stack exchange,提问作者Muskan wbst
相关产品推荐
相关产品推荐

