You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS中如何正确传递文件名?西里尔文名称丢失问题排查

解决西里尔文文件名上传后丢失的问题

前端(Expo)调整

  • 移除手动设置的Content-Type头部:axios会自动为FormData生成包含正确边界的multipart/form-data头部,手动设置会破坏边界解析逻辑,导致后端无法正确读取文件名。修改代码如下:
    export const updateTaskApi = async (
      id: string,
      taskData: TTask,
      files: TDataFilePicker[] | null,
    ) => {
      const headers = await createHeaders();
      // 删除该行:headers['Content-Type'] = 'multipart/form-data';
      console.log('headers', headers);
      const formData = new FormData();
      formData.append('taskData', JSON.stringify(taskData));
      if (files) {
        files.forEach((file) => {
          console.log('前端文件名:', file.filePickerName); // 确认文件名是否完整
          formData.append(
            'files',
            {
              uri: file.filePickerUri,
              type: file.filePickerType,
              name: file.filePickerName, // 仅保留name字段,移除冗余的fileName
            } as any,
          );
        });
      }
      return await axios.put(`${basicUrl}/tasks`, formData, {
        withCredentials: true,
        headers,
      });
    };
    

后端(NestJS/Multer)调整

Multer默认使用latin1编码解析文件名,无法处理西里尔文等非ASCII字符,需强制指定UTF-8编码:

  • 配置Multer拦截器的编码选项:

    import { FilesInterceptor } from '@nestjs/platform-express';
    import multer from 'multer';
    
    @Put()
    @HttpCode(HttpStatus.OK)
    @UseInterceptors(FilesInterceptor('files', undefined, {
      // 核心:设置文件名解析编码为UTF-8
      parserOptions: {
        encoding: 'utf8'
      },
      // 可选:若需保存文件,配置存储路径
      dest: './temp/uploads'
    }))
    async updateTask(
      @Body() body: TaskFormData,
      @Req() request: Request,
      @UploadedFiles() files?: Array<Express.Multer.File>,
    ) {
      await verifyAuthorization(request);
      console.log('处理后的文件:', files);
      // 后续业务逻辑保持不变
    }
    
  • 手动解码文件名(备选方案):
    如果上述配置无效,可从请求头中手动提取并解码UTF-8格式的文件名:

    // 辅助函数:从Content-Disposition头中提取UTF-8编码的文件名
    function getUtf8Filename(contentDisposition: string): string | null {
      const filenameMatch = contentDisposition.match(/filename\*=UTF-8''([\w\-\.а-яА-Я]+)/);
      if (filenameMatch) {
        return decodeURIComponent(filenameMatch[1]);
      }
      return null;
    }
    
    // 在控制器中应用
    async updateTask(
      @Body() body: TaskFormData,
      @Req() request: Request,
      @UploadedFiles() files?: Array<Express.Multer.File>,
    ) {
      await verifyAuthorization(request);
      // 修正文件名
      if (files) {
        files.forEach(file => {
          const contentDisposition = request.headers['content-disposition'] as string;
          const utf8Filename = getUtf8Filename(contentDisposition);
          if (utf8Filename) {
            file.originalname = utf8Filename;
          }
        });
      }
      console.log('修正后的文件名:', files?.map(f => f.originalname));
      // 后续业务逻辑保持不变
    }
    

验证步骤

  1. 前端打印file.filePickerName,确认原始文件名完整(如Аватар.jpg)。
  2. 后端查看请求头的content-disposition字段,确认包含filename*=UTF-8''%D0%90%D0%B2%D0%B0%D1%82%D0%B0%D1%80.jpg格式的编码文件名。
  3. 检查后端输出的originalname字段,确认西里尔文前缀已恢复。

内容的提问来源于stack exchange,提问作者juniorQuestion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 04:11:11