如何解决Python Bottle后端向Vue前端传输Excel文件的CORS错误?
解决Bottle后端返回Excel文件的CORS问题及前端下载处理
问题分析
你遇到的核心问题有两个:
- Bottle框架的
static_file返回文件时,CORS响应头被覆盖,导致浏览器触发跨域拦截 - 前端Fetch API的响应处理逻辑错误,误用了Axios风格的
response.data属性
后端修复方案
方案1:调整CORS插件,确保响应头不被覆盖
修改CORS插件逻辑,先获取请求处理后的响应对象,再为其添加CORS头,避免被static_file的默认头覆盖:
import json from bottle import run, static_file, request, response, Bottle class EnableCors(object): name = 'enable_cors' api = 2 def apply(self, fn, context): def _enable_cors(*args, **kwargs): # 先设置基础CORS头,处理OPTIONS预检请求 response.headers['Access-Control-Allow-Origin'] = '*' response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS' response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token' if context.method == 'OPTIONS': return response # 处理实际请求,获取响应后强制添加CORS头 resp = fn(*args, **kwargs) resp.headers['Access-Control-Allow-Origin'] = '*' resp.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS' resp.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token' return resp return _enable_cors app = Bottle() app.install(EnableCors()) @app.route("/generateExcel", method=["OPTIONS", "POST"]) def generateExcel(): print("here") file_dir = "/home/data/excelFiles/" file_name = "sample_excel.xlsx" # 添加download参数,让浏览器自动识别文件名 return static_file(filename=file_name, root=file_dir, download=file_name)
方案2:直接读取文件并手动设置响应头(绕过static_file的头覆盖问题)
如果方案1仍有问题,可以直接读取文件内容,手动配置所有响应头:
import json from bottle import run, request, response, Bottle class EnableCors(object): name = 'enable_cors' api = 2 def apply(self, fn, context): def _enable_cors(*args, **kwargs): response.headers['Access-Control-Allow-Origin'] = '*' response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS' response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token' if context.method == 'OPTIONS': return response return fn(*args, **kwargs) return _enable_cors app = Bottle() app.install(EnableCors()) @app.route("/generateExcel", method=["OPTIONS", "POST"]) def generateExcel(): print("here") file_dir = "/home/data/excelFiles/" file_name = "sample_excel.xlsx" file_path = f"{file_dir}{file_name}" # 设置文件类型和下载头 response.headers['Content-Type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' response.headers['Content-Disposition'] = f'attachment; filename="{file_name}"' with open(file_path, 'rb') as f: return f.read()
前端修复方案
核心修改点
- Fetch API的Response对象没有
data属性,需调用blob()方法获取二进制数据 - 添加响应状态校验,避免失败请求进入下载流程
- 从响应头提取后端返回的文件名(可选)
修改后的前端代码:
fetch(process.env.VUE_APP_BACKEND_ADDR + '/generateExcel', { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(parameters) } ).then((response) => { // 校验响应状态是否正常 if (!response.ok) { throw new Error(`请求失败,状态码:${response.status}`); } // 从响应头提取文件名(如果后端设置了Content-Disposition) const contentDisposition = response.headers.get('Content-Disposition'); let fileName = 'sample_excel.xlsx'; // 默认文件名 if (contentDisposition) { const matches = contentDisposition.match(/filename="?([^"]+)"?/); if (matches && matches[1]) { fileName = matches[1]; } } // 获取Blob数据并携带文件名 return response.blob().then(blob => ({ blob, fileName })); }).then(({ blob, fileName }) => { const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.setAttribute('download', fileName); document.body.appendChild(link); link.click(); // 清理资源,避免内存泄漏 URL.revokeObjectURL(url); document.body.removeChild(link); }).catch((error) => { console.error('下载出错:', error); const errorResponse = { status: "red", message: `${error} in generateExcel` }; console.log(errorResponse); })
内容的提问来源于stack exchange,提问作者Cathy Boothman
相关产品推荐
相关产品推荐

