Fetch无法接收Flask send_file()返回的响应头问题求助
问题解决:Flask后端传递Content-Disposition头,Vue前端Fetch无法获取的方案
核心原因
跨域请求场景下,浏览器默认仅允许前端获取简单响应头(如Content-Type、Cache-Control等),像Content-Disposition这类自定义/非标准头,必须由后端通过Access-Control-Expose-Headers明确指定暴露给前端,否则浏览器会隐藏这些头,导致前端无法读取。
解决方案
1. 使用Flask-CORS扩展(推荐)
这是最简便的跨域配置方式,能全局或针对路由指定要暴露的响应头:
- 先安装依赖:
pip install flask-cors - 全局配置(所有路由生效):
from flask import Flask from flask_cors import CORS app = Flask(__name__) # 配置CORS,暴露Content-Disposition头 CORS(app, expose_headers=["Content-Disposition"]) - 单路由配置(仅目标接口生效):
from flask_cors import cross_origin @app.route('/rdownload/<id>') @cross_origin(expose_headers=["Content-Disposition"]) def rdownload(id): # 你的文件返回逻辑,推荐用第一种send_file参数方式 return send_file(base_path + filename, as_attachment=True, download_name=filename)
2. 手动添加响应头(无需扩展)
如果不想引入Flask-CORS,可在每个需要的响应中手动添加暴露头:
from flask import make_response, send_file @app.route('/rdownload/<id>') def rdownload(id): response = make_response(send_file(base_path + filename, as_attachment=True, download_name=filename)) # 明确暴露Content-Disposition给前端 response.headers['Access-Control-Expose-Headers'] = 'Content-Disposition' return response
3. 前端适配(获取并解析文件名)
配置后端后,前端即可正常读取响应头,示例代码:
fetch('http://localhost:4999/rdownload/' + this.$route.params.id, { method: 'GET' }).then(res => { if (res.status === 500) { // 错误处理逻辑 return; } // 读取Content-Disposition头 const dispositionHeader = res.headers.get('Content-Disposition'); // 解析文件名(兼容带引号和不带引号的格式) const filenameMatch = dispositionHeader.match(/filename="?([^"]+)"?/); const filename = filenameMatch ? filenameMatch[1] : 'unknown-file'; res.blob().then((blob) => { // 用解析出的文件名触发下载 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); }) })
额外优化:处理中文文件名乱码
如果文件名包含中文,建议后端对文件名进行URL编码,避免乱码:
from urllib.parse import quote @app.route('/rdownload/<id>') @cross_origin(expose_headers=["Content-Disposition"]) def rdownload(id): encoded_filename = quote(filename) # 同时设置兼容旧浏览器的filename和标准的filename*字段 return send_file( base_path + filename, as_attachment=True, download_name=filename, headers={ 'Content-Disposition': f'attachment; filename="{filename}"; filename*=UTF-8''{encoded_filename}' } )
内容的提问来源于stack exchange,提问作者Insomnes
相关产品推荐
相关产品推荐

