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

Ale*_*gas 6 javascript

我正在开发一个 Web 应用程序,在前端使用 HTML + 纯 Javascript,并使用 Flask 作为后端。应用程序向服务器发送一些 ID,服务器应生成 PDF 文件格式的报告并将其发送回客户端。

我使用 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
Run Code Online (Sandbox Code Playgroud)

从命令行我可以测试端点并获得正确的文件,服务器控制台按预期打印 123 report_id:

curl --form reportid=123 http://localhost:5000/download_pdf >> output.pdf
Run Code Online (Sandbox Code Playgroud)

对于前端,我创建了一个调用 Javascript 函数的按钮:

<button id=pdf_button onclick="generatePdf()"> PDF </button>
Run Code Online (Sandbox Code Playgroud)

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);
    }

};
Run Code Online (Sandbox Code Playgroud)

在这种情况下,req.response 为 null。但是,对端点的调用已正确完成,因为后端控制台按预期打印了report_id。

已经尝试过:

最后,按下相关按钮后,Firefox 控制台会显示这 6 条消息(请观察前面代码中的 console.log() 调用):

2
0
null
4
0
null
Run Code Online (Sandbox Code Playgroud)

按下按钮时,Javascript 函数似乎被调用了两次。

我的目标是下载 PDF。我不知道我是否做错了什么;我会感谢对此的任何帮助。

Ale*_*gas 3

最后,我发现了问题所在,并将其发布以供记录。我认为这是不相关的,但<button>调用 Javascript 函数是在<form>. 我检查了表单在对端点的调用完成之前是否已更新,导致调用提前完成。

如果其他人需要这个作为例子,最终代码的片段如下:

HTML(选择和按钮都不是 的一部分<form>):

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

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();
}
Run Code Online (Sandbox Code Playgroud)

烧瓶:

@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')
Run Code Online (Sandbox Code Playgroud)

我不确定这是否是完成此任务的最佳或更优雅的方式,但到目前为止它对我有用。