前端发送文件,后端的nest.js如何接收文件名?

0 阅读2分钟

当我们在前端发送文件的时候,首先把文件的数据整合起来,在业务层汇聚到接口层,接口层使用参数接收一个或多个文件:

export function useUploadFile() {
  return useMutation({
    mutationFn: async ({
      bucket,
      files,
    }: {
      bucket: string;
      files: File[];
    }) => {
      const formData = new FormData();
      files.forEach((file) => {
        formData.append('files', file, encodeURIComponent(file.name));
      });

      const res = await http.post<UploadFileApiResponse>(
        `${uploadFilePath}/${bucket}`,
        formData,
        {
          headers: {
            'Accept-Charset': 'utf-8',
          },
        }
      );
      return res;
    },
  });
}

在上面的代码中,接收的是多个文件,使用File[]类型进行约束。

拿到文件后,先new一个FormData,用来发送文件,当文件的发送“容器”。那么多个文件就需要把它们都放入“容器”里才行。使用forEach进行放入。

使用formdata的append函数即可添加。注意append函数的第一个参数,那是文件的描述符号,可以随便写,当然,开发过程中需要遵守开发标准,后端接收的时候用什么描述符号,就填什么。

最关键的是第三个参数,第三个参数是调用了encodeURIComponent函数,这个函数会把字符转换为URL-encoded UTF-8 也就是URL编码的utf8格式。

那么就需要说一下nestjs的文件接收的编码转换的坑了。

使用nestjs接收文件时,使用:

@Post('upload/:bucket')
  @UseInterceptors(FilesInterceptor('files', 100))
  async uploadFile(
    @Param('bucket') bucket: string,
    @UploadedFiles() files: Express.Multer.File[],
  ) {}

这样的方式接收文件。

问题在于它接收的时候,会把文件名从utf8转为ISO-8859-1 (Latin-1 ) 编码,这导致文件的中文名会变成:“çµå¶é”这样的字符。

所以为了避免这样的问题,就需要

formData.append('files', file, encodeURIComponent(file.name));

在这里调用encodeURIComponent对原始的文件名进行编码,这样到了后端,文件的编码形式就不会改变。

后端使用文件名时,如下方法即可:

        files.map(async (f) => {
            // 解码前端发送的url编码形式,正确的将文件名传输过来
            const newfilename = decodeURIComponent(f.originalname);

            // 为minIO的每个文件添加一个单独的uuid,防止文件名重名,在获取文件信息以及下载文件时予以清除
            const UUID = randomUUID();
            const safeFilename = newfilename.replace(/[“”#]/g, '').trim();
            this.minioService.validateFile(f);

            // 保存文件信息到mysql数据库
            await this.fileService.saveFile(
              safeFilename,
              UUID,
              f.mimetype,
              f.size,
            );

            // 分片上传文件到 MinIO
            await this.minioService.uploadFile(UUID, f, bucket);

            // 更新任务的已上传文件数
            const task = uploadTasks.get(taskId);
            if (task) task.uploadedFiles++;
          }),
        );

可以看到在第三行,调用了decodeURIComponent对文件的原始名称进行解码,即可得到正确的文件名。