简体   繁体   English

如何使用send_file()接收从Flask发送的Javascript文件

[英]How to receive files in Javascript sent from Flask using send_file()

I am developing a web application using HTML + plain Javascript in the frontend, and Flask as backend. 我正在使用前端HTML +纯Java和后端Flask开发Web应用程序。 The application sends some ID to the server, the server should generate a report as PDF file and send it back to the client. 应用程序将一些ID发送到服务器,服务器应将报告生成为PDF文件并将其发送回客户端。

I am using Flask for the backend and I have created the following endpoint: 我将Flask用于后端,并且创建了以下端点:

    @app.route("/download_pdf", methods=['POST'])
    def download_pdf():
        if request.method == 'POST':
            report_id = request.form['reportid']
            print(report_id) //Testing purposes.
            // do some stuff with report_id and generate a pdf file.
            return send_file('/home/user/report.pdf', mimetype='application/pdf', as_attachment=True)
// I already tried different values for mimetype and as_attachment=False

From the command line I can test the endpoint and I get the right file, and the server console prints the 123 report_id as expected: 从命令行,我可以测试端点并获得正确的文件,服务器控制台将按预期方式输出123 report_id:

curl --form reportid=123 http://localhost:5000/download_pdf >> output.pdf

For the frontend side I created a button that calls a Javascript function: 对于前端,我创建了一个调用Javascript函数的按钮:

<button id=pdf_button onclick="generatePdf()"> PDF </button>

The Javascript function looks like this: Javascript函数如下所示:

function generatePdf(){
    var report_list = document.getElementById("report_list")

    if (report_list.selectedIndex < 0){
        alert("Please, select a report.");
    }else{
        var req = new XMLHttpRequest();

        req.open("POST", "/download_pdf", true);
        req.responseType = "document";
        req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        req.onreadystatechange = function(){
            console.log(req.readyState)
            console.log(req.status)
            console.log(req.response)
            var link = document.createElement('a')
            link.href = req.response;
            link.download="report.pdf"
            link.click()

        }
        var selected_value = report_list.options[report_list.selectedIndex].value;
        var params="reportid="+selected_value;
        req.send(params);
    }

};

req.response is null in this case. 在这种情况下,req.response为null。 However, the call to the endpoint has been done correctly, as the backend console prints the report_id as expected. 但是,对端点的调用已正确完成,因为后端控制台按预期输出了report_id。

Already tried: 已经尝试过:

Lastly, the Firefox console shows these 6 messages after pressing the related button (please, observe the console.log() calls in the previous code): 最后,在按下相关按钮后,Firefox控制台显示这6条消息(请注意前面代码中的console.log()调用):

2
0
null
4
0
null

It seems that the Javascript function has been called twice when the button is pressed. 似乎当按下按钮时,Javascript函数已被调用两次。

My goal is to get the PDF downloaded. 我的目标是下载PDF。 I don't know if what am I doing wrong; 我不知道我在做什么错; I'd thank any help on this. 我对此表示感谢。

Your ajax settings are wrong, they should be like these 您的ajax设置错误,应该像这样

req.open("POST", "/download_pdf", true);
req.responseType = "blob";

req.onreadystatechange = function() {
  console.log(req.readyState)
  console.log(req.status)
  const blob = new Blob([req.response]);
  const url = window.URL.createObjectURL(blob);

  const link = document.createElement('a')
  link.href = url
  link.download = "report.pdf"
  link.click()
}

The response type should be blob and when you get the response, parse it as a blob. 响应类型应为blob,当您获得响应时,将其解析为blob。 After some time, remove the link 一段时间后,删除链接

setTimeout(() => {
    window.URL.revokeObjectURL(url);
    link.remove();
}, 100);

Finally, I found what the problem was and I post this for the record. 最终,我发现了问题所在,并将其发布以供记录。 I thought it was unrelated, but the <button> calling the Javascript function was inside a <form> . 我以为这没有关系,但是调用Javascript函数的<button><form>内部。 I checked that the form was updated before the call to the endpoint finished, causing the call to finish prepaturely. 我检查了对端点的调用完成之前表单是否已更新,从而导致调用提前完成。

If somebody else needs this as example, a snipet of the final code is as follows: 如果其他人需要此示例,则最终代码的摘要如下:

HTML (both the select and button are not part of a <form> ): HTML(select和button都不是<form> ):

<select id="report_list" size=20> ... </select> ... <button id="pdf_button" onclick="generatePdf()"> PDF </button>

Javascript: Javascript:

function generatePdf(){
    var report_list = document.getElementById("report_list");
    var req = XMLHttpRequest();
    var selected_value = report_list.options[report_list.selectedIndex].value;

    req.open("POST", "/reports/"+selected_value+"/pdf", true);
    req.responseType = "blob";
    req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    req.onreadystatechange = function(){
    if (this.readyState == 4 && this.status == 200){
        var blob = new Blob([this.response], {type: "application/pdf"});
        var url = window.URL.createObjectURL(blob);
        var link = document.createElement('a');
        document.body.appendChild(link);
        link.style = "display: none";
        link.href = url;
        link.download = "report.pdf";
        link.click();

        setTimeout(() => {
        window.URL.revokeObjectURL(url);
        link.remove(); } , 100);
    }
    };

    req.send();
}

Flask: 烧瓶:

@app.route("/reports/<id>/pdf", methods=['POST'])
def get_pdf(id):
    if request.method == 'POST':
        return send_file(get_pdf_path(id), mimetype='application/pdf')

I am not sure if this is the best or more elegant way to get this done, but so far it works for me. 我不确定这是否是完成此任务的最好或更优雅的方法,但是到目前为止,它对我还是有效的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM