NestJS 上传文件中文名乱码

问题

服务端使用的是NestJS,在处理上传需求的时候,发现multipart/form-data上传文件的文件名是中文时,服务器读取到的是乱码

英文是正常的,中文是乱码的

定位问题

环境

  • Node: 22.14.0
  • NetsJS: 10.4.2

Express 的 Multer 包。

代码排查

由于上传文件使用的是 Multer ,因此找到了 Github 上面 Multer 的代码仓库,然后在 Issues 中搜索,发现了类似的情况:

Issue with UTF-8 characters in filename · Issue #1104 · expressjs/multer (github.com)

这个 issue 中提到了另一个包 busboy,Multer 就是通过这个包来解析 FormData 的,原来是这个 busboy 的问题,在该仓库的 Issues 中也能发现有人提了这个问题:

Parsing fails if filename contains UTF-8 characters · Issue #20 · mscdex/busboy (github.com)

busboy 将配置项defParamCharset的默认值从utf8改为了latin1,从而导致了这个问题(居然是一个 patch 更新导致很多人的代码出问题了然后就被指责了),需要手动设置一下。问题是这个 busboy 是由 Multer 负责创建的,同时 Multer 并没有提供defParamCharset这个选项,更别说在内部使用了 Multer 的 NestJS 了。

解决问题

目前 issue 中提供的方法和搜索引擎中提供的方法是类似的:

ts 复制代码
file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8');

在 NestJS 中,我们可以自定义一个管道 Pipe 来处理这个问题。

首先定义一个管道common/pips/file-name-encode.pipe.ts

ts 复制代码
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';  
  
@Injectable()  
export class FileNameEncodePipe implements PipeTransform {  
transform(value: Express.Multer.File, metadata: ArgumentMetadata) {  
if (!/[^\u0000-\u00ff]/.test(value.originalname)) {  
value.originalname = Buffer.from(value.originalname, 'latin1').toString(  
'utf8',  
);  
}  
return value;  
}  
}

然后在controller就可以用了

ts 复制代码
import { FileNameEncodePipe } from '@/common/pipes/file-name-encode.pipe';

async uploadFile(
    @UploadedFile(
      new FileNameEncodePipe(),
      new ParseFilePipe({
        validators: [
          new MaxFileSizeValidator({ maxSize: 30 * 1024 * 1024 }),
          new FileTypeValidator({ fileType: 'application/pdf' }),
        ],
      }),
    )
    file: Express.Multer.File,
  ) {
    return this.uploadService.handleFileUpload(file);
  }

这里是对原文件名进行一个正则判断,如果原来的文件名可以通过latin1方式正确解码出来,就不处理。

至此,浏览器和 Apifox 测试都正常了。

相关推荐
高频因子挖掘机6 分钟前
实时行情为什么不应该逐只股票请求?批量查询与标的池管理实践
后端·github·api
程序猿DD5 小时前
OctaFuse Gateway 2.15.0:MCP 聚合、向量 Embeddings 与官方目录同步
后端·agent
余槐i8 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
需要8268 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
凤山老林9 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
伯恩bourne10 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
张彦峰ZYF10 小时前
停顿短不等于延迟低:G1、ZGC 与低延迟服务的目标分野、代价账本与选型标定
后端·回收暂停、应用停止总时长·到达安全点、分配停滞·屏障吃吞吐、并发线程吃核心·并发窗口吃堆余量·分区式靠挑回收集兑现目标·染色指针式不支持压缩引用
pride.li11 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉
upala11 小时前
【仓颉语言入门 · 第二篇 第 6 课】
后端
罗丹丹Luna11 小时前
移动端显示“已提交”以后:SAP 与低代码集成该怎样暴露真实状态
后端·架构