前端无法获取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
相关产品推荐
相关产品推荐

