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

前端无法获取Content-Disposition响应头,求原因解析

问题描述

我在后端实现了如下API接口:

@ApiPost('download/:id', { type: OutputStatusDto, 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);

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

  res.end(buffer);
}

前端侧编写了如下代码:

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

遗憾的是,Content-Disposition响应头在前端不可见,我原本想通过它提取filename,请问这是为什么?


原因与解决方案

这是**跨域资源共享(CORS)**的限制导致的:

  • 浏览器默认仅向前端JavaScript暴露一组「安全响应头」(如Content-Type、Cache-Control等),Content-Disposition不在默认暴露列表内。
  • 当前端与后端处于不同域名时,后端必须显式配置Access-Control-Expose-Headers响应头,将Content-Disposition加入允许暴露的列表,前端才能在response.headers中获取到它。

后端修改示例(NestJS)

单接口配置

在接口中新增暴露头的配置:

async downloadFile(@Param() params: InputIdDto, @Res() res: Response) {
  // 原有业务逻辑...

  // 新增:允许前端访问Content-Disposition头
  res.set('Access-Control-Expose-Headers', 'Content-Disposition');
  
  res.set({
    'Content-Type': 'application/octet-stream',
    'Content-Disposition': `attachment; filename=${file.originalname}`,
    'Content-Length': buffer.length,
  });

  res.end(buffer);
}

全局CORS配置

如果需要多个接口都支持,可在全局CORS配置中统一设置:

// main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors({
    exposedHeaders: ['Content-Disposition'], // 全局暴露该响应头
    // 其他CORS相关配置...
  });
  await app.listen(3000);
}
bootstrap();

前端提取文件名示例

后端配置完成后,前端可从响应头中提取文件名:

export const downloadFile = (id: string) =>
  axios
    .get<any>(`${process.env.NEXT_PUBLIC_API_URL}/stripe/file/download/${id}`, {
      responseType: 'blob',
    })
    .then((res: AxiosResponse<any>) => {
      const disposition = res.headers['content-disposition'];
      let filename = 'default_file';
      if (disposition) {
        // 匹配Content-Disposition中的文件名
        const matches = disposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
        if (matches?.[1]) {
          filename = matches[1].replace(/['"]/g, '');
        }
      }
      return {
        data: res.data,
        filename,
      };
    });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 07:16:05