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

Next.js(Axios)与Nest.js文件下载异常:响应数据为空对象

问题

我在Nest.js中实现了文件下载接口:

@ApiGet(':id/download', { type: OutputStatusDto, description: 'Download file from URL' })
async downloadFile(@User() user: UserEntity, @Param() params: InputIdDto, @Res() res: Response) {
  const file = await this._fileService.findOne({ where: { id: params.id }, relations: { user: true } });
  // /* For now, allow downloading only files that are not assigned to any user */
  // if (user.role !== Roles.ADMIN && file.user && file.user.id !== user.id)
  //   throw new BadRequestException(ErrorMessages.UNAUTHORIZED_ACTION);

  const { buffer } = await this._fileService.downloadFile(file.url);

  res.set({
    'Content-Type': 'application/octet-stream',
    'Content-Disposition': `attachment; filename=${file.originalname}`,
    'Content-Length': buffer.length,
  });

  res.end(buffer);
}


async downloadFile(url: string): Promise<{ buffer: Buffer }> {
  const response = await axios.get(url, { responseType: 'arraybuffer' });

  return { buffer: Buffer.from(response.data) };
}

在Next.js应用中编写了对应的下载逻辑:

export const useDownloadFile = () => {
  return useMutation({
    mutationFn: downloadFile,
    onSuccess(data) {
      const blob = new Blob([data.data]);

      const url = window.URL.createObjectURL(blob);

      const a = document.createElement('a');
      a.href = url;
      a.download = data.filename;
      document.body.appendChild(a);
      a.click();
      a.remove();
    },
  });
};

export const downloadFile = (data: { id: string }) =>
  axios
    .get<Blob>(`${process.env.NEXT_PUBLIC_API_URL}/file/${data.id}/download`, {
      responseType: 'blob',
    })
    .then((res: AxiosResponse<Blob>) => {
      console.log('res', res);
      let filename = 'file';
      const contentDisposition = res.headers['content-disposition'];
      if (contentDisposition) {
        const filenameMatch = contentDisposition.match(
          /filename="?(.+?)"?(\;|$)/
        );
        if (filenameMatch.length > 1) {
          filename = filenameMatch[1];
        }
      }

      return {
        data: res.data,
        filename: filename,
      };
    });

实际调用时,前端响应中的data为空对象{},无法完成文件下载,但后端的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 ... 101467 more bytes>

可能的原因及解决方法

  • Nest.js手动响应与序列化拦截器冲突
    使用@Res()手动管理响应时,如果全局配置了ClassSerializerInterceptor,拦截器会尝试序列化Buffer,导致响应体被破坏。解决方式:

    1. 在控制器方法上添加@SkipInterceptor(ClassSerializerInterceptor)跳过序列化;
    2. 改用Nest.js内置的StreamableFile返回文件,避免手动操作响应:
      import { StreamableFile } from '@nestjs/common';
      // ...
      async downloadFile(/* 参数 */) {
        // ...获取buffer逻辑
        return new StreamableFile(Buffer.from(buffer), {
          type: 'application/octet-stream',
          disposition: `attachment; filename=${file.originalname}`,
        });
      }
      
  • CORS未暴露自定义响应头
    后端CORS配置如果未开放Content-Disposition等头,会导致前端无法正确解析响应,甚至影响响应体读取。在Nest.js的main.ts中修改CORS配置:

    app.enableCors({
      exposedHeaders: ['Content-Disposition', 'Content-Length'],
      // 其他CORS配置项
    });
    
  • Axios响应类型解析问题
    虽然设置了responseType: 'blob',但部分场景下Axios对某些Content-Type的解析存在异常。可以改为请求arraybuffer再手动转Blob:

    // 前端downloadFile函数修改
    axios.get(`${process.env.NEXT_PUBLIC_API_URL}/file/${data.id}/download`, {
      responseType: 'arraybuffer',
    }).then(res => {
      const blob = new Blob([res.data], { type: res.headers['content-type'] });
      // ...解析文件名等逻辑
      return { data: blob, filename };
    });
    
  • Next.js环境变量配置错误
    检查process.env.NEXT_PUBLIC_API_URL是否正确配置,确保请求地址指向正确的后端服务。可以在控制台打印请求URL验证地址是否正确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 23:22:24