【问题标题】:download excel by using Ajax andFlask使用Ajax和Flask下载excel
【发布时间】:2020-10-05 20:53:03
【问题描述】:

我正在尝试使用 Ajax 调用从烧瓶中下载 excel。它显示响应代码为 200,但 excel 未下载,错误消息如下。

Ajax 请求:

$("#genExcel").on("点击", function() { var xhttp = new XMLHttpRequest();

                // Data to post
                var dataarray = {};

                // Use XMLHttpRequest instead of Jquery $ajax

                xhttp.onreadystatechange = function() {
                    var a;
                    if (xhttp.readyState === 4 && xhttp.status === 200) {
                        // Trick for making downloadable link
                        a = document.createElement('a');
                        const objectURL = window.URL.createObjectURL(xhttp.response);
                        a.href = objectURL

                        //const objectURL = URL.createObjectURL(object)

                        // Give filename you wish to download
                        a.download = "test-file.xlsx";
                        a.style.display = 'none';
                        document.body.appendChild(a);
                        a.click();
                    }
                };
                // Post data to URL which handles post request
                xhttp.open("POST", '/genexcel');
                xhttp.setRequestHeader("Content-Type", "application/json");
                // You should set responseType as blob for binary responses
                //xhttp.responseType = 'blob';
                xhttp.send(JSON.stringify(dataarray));
            });

烧瓶功能:

@app.route('/genexcel', methods=["GET", "POST"])
def createExcel():
    if request.method == 'POST':
        data = request.json  
        # process json data        
        return send_file(strIO, attachment_filename='test.xlsx',  as_attachment=True)

错误:

1 [仅报告] 拒绝将字符串评估为 JavaScript,因为“unsafe-eval”不是以下内容安全策略指令中允许的脚本来源:“script-src * blob:”。

2 未捕获的类型错误:无法在“URL”上执行“createObjectURL”:未找到与提供的签名匹配的函数。

at XMLHttpRequest.xhttp.onreadystatechange

【问题讨论】:

    标签: javascript python jquery ajax flask


    【解决方案1】:

    这是一个使用 fetch API 的示例。第一个按钮只是进行直接的 JS 下载。第二个按钮使用 Flask 路由进行下载。希望对您有所帮助。

    index.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Test</title>
      </head>
      <body>
        Testing
        <button id="genExcel">Direct Download</button>
        <button id="genExcelFlask">Flask Download</button>
      </body>
    
      <script>
        var btn = document.getElementById("genExcel");
        var btnFlask = document.getElementById("genExcelFlask");
    
        btn.addEventListener("click", (e) => {
          fetch("https://jsonplaceholder.typicode.com/todos/1")
            .then((resp) => resp.blob())
            .then((blob) => {
              const url = window.URL.createObjectURL(blob);
              const a = document.createElement("a");
              a.style.display = "none";
              a.href = url;
              // the filename you want
              a.download = "todo-1.json";
              document.body.appendChild(a);
              a.click();
              window.URL.revokeObjectURL(url);
              alert("your file has downloaded!"); // or you know, something with better UX...
            })
            .catch(() => alert("oh no!"));
        });
    
        btnFlask.addEventListener("click", (e) => {
          fetch("{{ url_for('createExcel') }}")
            .then((resp) => resp.blob())
            .then((blob) => {
              const url = window.URL.createObjectURL(blob);
              const a = document.createElement("a");
              a.style.display = "none";
              a.href = url;
              // the filename you want
              a.download = "test.xlsx";
              document.body.appendChild(a);
              a.click();
              window.URL.revokeObjectURL(url);
              alert("your file has downloaded!"); // or you know, something with better UX...
            })
            .catch(() => alert("oh no!"));
        });
      </script>
    </html>
    
    

    烧瓶功能

    from flask import Flask, render_template, request, url_for, send_file
    
    @app.route('/genexcel', methods=["GET", "POST"])
    def createExcel():
        if request.method == 'POST':
            data = request.json
            print(data)
            # process json data
    
        file_path = 'static/files/test.xlsx'
        return send_file(file_path, attachment_filename='test.xlsx', as_attachment=True)
    

    【讨论】:

    • 它正在下载 excel 并显示警告消息,因为文件已损坏。在我的代码中..我将数组传递给flask函数并将该数据插入excel然后下载它。你能尝试将js数组数据传递给flask函数并从内存而不是物理位置生成excel吗?
    • 所以你希望JS将am数组传递给flask函数。在该函数中,您希望获取传递给它的数据并将其放入一个新的 excel 文件中,然后返回要下载的 excel 文件......对吗?你有数组数据的例子吗?
    • 是的..我想传递数组数据,它应该即时插入到excel中,不需要从物理路径读取excel。示例数组是一个嵌套数组,例如:dataArray= [[1,A,100],[2,B,200]] 在输出 excel 中每个数组将被插入到 excel 的一行中。
    【解决方案2】:

    希望我对您的理解正确。这是一个使用您提供的数据数组的非常简单的示例。您可以根据需要进行修改:

    烧瓶函数

    from flask import Flask, render_template, request, url_for, send_file
    import xlsxwriter
    
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        return render_template('index.html')
    
    @app.route('/genexcel', methods=["GET", "POST"])
    def createExcel():
        if request.method == 'POST':
            data = request.get_json(force=True)
            # process json data
            createExcel(data['data'])
    
        file_path = 'static/files/test.xlsx'
        return send_file(file_path, attachment_filename='test.xlsx', as_attachment=True)
    
    def createExcel(data):
        workbook = xlsxwriter.Workbook('static/files/test.xlsx')
        worksheet = workbook.add_worksheet()
    
        row_no = 0
        col_no = 0
        for row in data:
            col_no = 0
            for entry in row:
                worksheet.write(row_no, col_no, entry)
                col_no += 1
            row_no += 1
    
        workbook.close()
    
    app.run(debug=True, port=5010)
    

    index.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Test</title>
      </head>
      <body>
        Testing
        <button id="genExcel">Direct Download</button>
        <button id="genExcelFlask">Flask Download</button>
      </body>
    
      <script>
        var btn = document.getElementById("genExcel");
        var btnFlask = document.getElementById("genExcelFlask");
        var dataArray = {
          data: [
            [1, "A", 100],
            [2, "B", 200],
          ],
        };
    
        btn.addEventListener("click", (e) => {
          fetch("https://jsonplaceholder.typicode.com/todos/1")
            .then((resp) => resp.blob())
            .then((blob) => {
              const url = window.URL.createObjectURL(blob);
              const a = document.createElement("a");
              a.style.display = "none";
              a.href = url;
              // the filename you want
              a.download = "todo-1.json";
              document.body.appendChild(a);
              a.click();
              window.URL.revokeObjectURL(url);
              alert("your file has downloaded!"); // or you know, something with better UX...
            })
            .catch(() => alert("oh no!"));
        });
    
        btnFlask.addEventListener("click", (e) => {
          console.log(JSON.stringify(dataArray));
    
          fetch("{{ url_for('createExcel') }}", {
            method: "post",
            body: JSON.stringify(dataArray),
          })
            .then((resp) => resp.blob())
            .then((blob) => {
              const url = window.URL.createObjectURL(blob);
              const a = document.createElement("a");
              a.style.display = "none";
              a.href = url;
              // the filename you want
              a.download = "test.xlsx";
              document.body.appendChild(a);
              a.click();
              window.URL.revokeObjectURL(url);
              alert("your file has downloaded!"); // or you know, something with better UX...
            })
            .catch(() => alert("oh no!"));
        });
      </script>
    </html>
    
    

    【讨论】:

    • @p-s-rao 这对你有用吗?我是否正确理解了您的要求?
    • 在 fetch 方法中添加标题后:{ 'Content-Type': 'application/json' } 它正在工作。在烧瓶函数而不是物理路径中,我将文件保存在内存中,并且可供用户使用。非常感谢。
    猜你喜欢
    • 2014-12-08
    • 1970-01-01
    • 1970-01-01
    • 2022-10-12
    • 2019-01-11
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多