GET请求后Fetch API无法正确获取响应头中的Content-Disposition文件名
问题核心原因
- 你的Django视图中
Content-Disposition响应头存在语法错误:设置值里filename="foo.xlsx缺少闭合的双引号,会导致浏览器解析该响应头异常。 - 你当前使用的
window.location.assign(blobUrl)下载方式本身无法继承原请求的响应头信息:通过createObjectURL生成的blob链接是浏览器本地的临时资源链接,和你之前发起的Fetch请求的响应上下文完全独立,浏览器访问这个临时链接时不会也不可能知道原响应头里配置的文件名,自然就会生成随机文件名。 - 你看到Chrome开发者工具默认只展示
Content-Length和Content-Type属于正常现象,Chrome网络面板默认只展示简单响应头,点击「查看全部响应头」就能看到存在的Content-Disposition,和你console打印的结果一致。
解决方案
第一步:修复Django侧响应头语法错误
你之前添加的Access-Control-Expose-Headers配置是正确的,无需修改,仅需要补全Content-Disposition的闭合引号即可:
response = FileResponse(buffer, as_attachment=True, filename="foo.xlsx") response['Content-Type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' # 补全filename后的闭合双引号 response['Content-Disposition'] = 'attachment; filename="foo.xlsx"' response['Access-Control-Expose-Headers'] = 'Content-Disposition' return response
第二步:修改前端Fetch下载逻辑
受浏览器机制限制,你没办法完全不写额外JS就通过blob跳转的方式拿到正确文件名,必须手动提取响应头的文件名,通过动态a标签的download属性指定文件名,这是标准实现逻辑,不属于多余代码:
fetch(`{% url 'export_xls' %}?${urlParams}`,{ method: 'GET', mode: 'cors', headers: { "X-CSRFToken": '{{ csrf_token }}', } }) .then(res => { // 从响应头提取文件名 const disposition = res.headers.get('Content-Disposition'); let filename = 'default.xlsx'; if (disposition && disposition.includes('attachment')) { const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/; const matches = filenameRegex.exec(disposition); if (matches?.[1]) { filename = matches[1].replace(/['"]/g, ''); } } // 把blob和文件名一起传递 return Promise.all([res.blob(), filename]); }) .then(([blob, filename]) => { const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); // 清理临时资源 window.URL.revokeObjectURL(url); document.body.removeChild(a); })
内容的提问来源于stack exchange,提问作者dsax7
相关产品推荐
相关产品推荐

