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

使用NestJS实现文件下载后无法打开文件的问题排查

问题

我在NestJS应用中编写了如下Controller:

@ApiPost('download/:id', { type: Blob, description: 'Download file from URL' })
async downloadFile(@Param() params: InputIdDto, @Res() res: Response) {
  const file = await this._fileService.findOne({ where: { id: params.id } });
  const { buffer } = await this._fileService.downloadFile(file.url);

  console.log(buffer);

  res.set({
    'Content-Type': 'application/octet-stream',
    'Content-Disposition': `attachment; filename=${file.originalname}`, // 客户端无法获取文件名
    'Content-Length': buffer.length,
  });

  res.end(buffer);
}

控制台中buffer输出为:

<Buffer 25 50 44 46 2d 31 2e 34 0a 25 d3 eb e9 e1 0a 31 20 30 20 6f 62 6a 0a 3c 3c 2f 54 69 74 6c 65 20 28 4d 61 74 68 57 69 7a 61 72 64 73 29 0a 2f 43 72 65 ... 15140 more bytes>

在NextJS应用中对应代码如下:

export const downloadFile = (data: { id: string; filename: string }) =>
  axios
    .post<Blob>(`${process.env.NEXT_PUBLIC_API_URL}/file/download/${data.id}`, {
      responseType: 'blob',
    })
    .then((res: AxiosResponse<Blob>) => res.data);


export const useDownloadFile = () => {
  return useMutation({
    mutationFn: downloadFile,
    onSuccess(data, variables, context) {
      console.log('Blob', data);
      const url = window.URL.createObjectURL(new Blob([data]));
      console.log(data, url);

      const a = document.createElement('a');
      a.href = url;
      a.download = 'download-file';
      document.body.appendChild(a);
      a.click();
      a.remove();
    },
    onError(error, variables, context) {
      console.log('Error', error);
    },
  });
};

应用控制台输出为:

Blob %PDF-1.4
%����
1 0 obj
<</Title (Title)
/Creator (Mozilla/5.0 \(Windows NT 10.0; Win64; x64\) AppleWebKit/537.36 \(KHTML, like Gecko\) HeadlessChrome/125.0.0.0 Safari/537.36)
/Producer (Skia/PDF m125)
/CreationDate (D:20240608210121+00'00')
/ModDate (D:20240608210121+00'00')>>
endobj
3 0 obj
<</ca 1
/BM /Normal>>
endobj
6 0 obj
<</Filter /FlateDecode
...

现在文件能下载,但无法打开(比如PDF)或为空,其他格式文件也有同样问题,请问代码哪里出错了?


解决方案

你的代码存在两个核心问题,逐一修正即可解决:

1. Axios请求配置错误

axios.post的参数顺序是url, data, config,你把responseType: 'blob'放在了第二个参数(请求体位置),导致配置不生效,Axios默认把响应解析成了字符串而非Blob。

修正后的downloadFile函数:

export const downloadFile = (data: { id: string; filename: string }) =>
  axios
    .post<Blob>(
      `${process.env.NEXT_PUBLIC_API_URL}/file/download/${data.id}`,
      null, // POST请求无需请求体时传null占位
      { responseType: 'blob' } // 正确放在第三个参数的配置项中
    )
    .then((res: AxiosResponse<Blob>) => res.data);

2. 重复包装Blob导致文件损坏

前端onSuccess中,data已经是Axios返回的Blob对象,你再用new Blob([data])包装一次,相当于把Blob当成字符串数组处理,破坏了原文件的二进制结构。

修正后的onSuccess逻辑:

onSuccess(data, variables, context) {
  console.log('Blob', data);
  const url = window.URL.createObjectURL(data); // 直接使用返回的Blob对象

  const a = document.createElement('a');
  a.href = url;
  a.download = variables.filename; // 可直接使用传入的文件名,无需写死
  document.body.appendChild(a);
  a.click();
  a.remove();
  window.URL.revokeObjectURL(url); // 释放URL对象,避免内存泄漏
}

可选优化(NestJS端)

  • 把res.end(buffer)改成res.send(buffer),NestJS的响应处理会更规范
  • 对文件名做URL编码,避免特殊字符导致的文件名异常:
'Content-Disposition': `attachment; filename=${encodeURIComponent(file.originalname)}`

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 17:34:51