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

如何解决Python Bottle后端向Vue前端传输Excel文件的CORS错误?

解决Bottle后端返回Excel文件的CORS问题及前端下载处理

问题分析

你遇到的核心问题有两个:

  1. Bottle框架的static_file返回文件时,CORS响应头被覆盖,导致浏览器触发跨域拦截
  2. 前端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()

前端修复方案

核心修改点

  1. Fetch API的Response对象没有data属性,需调用blob()方法获取二进制数据
  2. 添加响应状态校验,避免失败请求进入下载流程
  3. 从响应头提取后端返回的文件名(可选)

修改后的前端代码:

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 06:37:06