简体   繁体   English

Flask send_file 用 JavaScript 完成

[英]Flask send_file done with JavaScript

I have this flask app route:我有这个 flask 应用程序路线:

@app.route('/generatecleanbudgetfile', methods=['GET', 'POST'])
def clean_budget():
    file = request.files.get('data_file')
    app.logger.info('Budget Formatting request has started')
    try:
        if request.method == 'POST':
            file = request.files.get('data_file')
            file.seek(0)
            buffer = budget_cleaner(file)
            buffer.seek(0)
            app.logger.info('Conversion Complete')
            return send_file(
            buffer,
            as_attachment=True,
            attachment_filename=f'stripped_budget_{dt.today().strftime("%m.%d.%Y")}.xlsx',
            mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
            )
    except:
        app.logger.error(f"Budget formatting failed on {file}")
        return render_template('error_type.html', title='Unable to process uploaded budget')  

My html:我的 html:

{% extends "base.html" %}
{% block head %}
{% endblock %}
{% block content %}
<div id="vo_budget_file_settings">
    {# <a href="/generatecleanbudgetfile" class="btn btn-primary">Upload Final CRO Budget File</a> #}
    <p id="uploadPara">Please upload the final CRO budget File</p>
    <form class="" action="/generatecleanbudgetfile" method=POST enctype=multipart/form-data>
        <input type="file" name="data_file" accept=".xls, .xlsx, .xlsm"/>
        <input type="submit" value="Begin Format" onclick="loading();"/>
    </form>
</div>
<!-- funtion to show css spinner on button click -->
<script type="text/javascript">
    function loading(){
      $(".loader").show();
    }
    </script>

I understand that flask cannot both render_template and send_file since it can only return a single item.我知道 flask 不能同时render_templatesend_file因为它只能返回一个项目。

Question:问题:

How would I go about downloading a file via JavaScript instead so I could use my return to render a new template?我将如何通过 JavaScript 下载文件,以便我可以使用我的return来呈现新模板?

I'm wanting to replace this piece:我想更换这件作品:

return send_file(
            buffer,
            as_attachment=True,
            attachment_filename=f'stripped_budget_{dt.today().strftime("%m.%d.%Y")}.xlsx',
            mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
            )

You should not send two responses to the same request.您不应该对同一个请求发送两个响应。 Instead save the file and serve a route to download it in the rendered template.而是保存文件并提供路径以在呈现的模板中下载它。

I recommend checking this:我建议检查这个:

https://stackoverflow.com/a/34010520/10533106 https://stackoverflow.com/a/34010520/10533106

You cannot return multiple responses to a single request.您不能对单个请求返回多个响应。 Instead, generate and store the files somewhere, and serve them with another route.相反,在某处生成并存储文件,并使用另一条路线为它们提供服务。 Return your rendered template with a url for the route to serve the file.返回带有 url 的渲染模板,用于提供文件的路径。

Flask can't do both things(render & send file) in a single request. Flask 不能在一个请求中同时做这两件事(渲染和发送文件)。

But you can have an alternative solution↓但是你可以有另一种解决方案↓

In your generatecleanbudgetfile request:在您的generatecleanbudgetfile请求中:

1. Generate a file and save it in server

2. Provide a file link in your html

3. just render_template in this request

So, when the user get the response, they can click on download link to retrieve the file generated in the first step.因此,当用户收到响应时,他们可以单击下载链接来检索第一步生成的文件。

To download the file with JS, you could encode it and include it in your template as a string literal inside a JS Blob object, then save that Blob as soon as the page renders.要使用 JS 下载文件,您可以对其进行编码并将其作为字符串文字包含在模板中,并在 JS Blob object 中,然后在页面呈现后立即保存该 Blob。 Something like this in your template for success, where you pass your encoded file contents into the file_contents template variable:在你的模板中这样的东西成功,你将编码的文件内容传递给file_contents模板变量:

<body>
   Page content here
   <script>
       const myBlob = new Blob(["{{file_contents}}"], {type:"your-file's-mime-type"})
       // then google "how to save js blob to local file". 
       // This other post might help: https://stackoverflow.com/questions/25547475/save-to-local-file-from-blob
  </script>
</body>

To further illustrate as an example, making a blob that has the plaintext contents "Hello World" would go like new Blob(["Hello World"], {type:"text/plain"}) .为了进一步举例说明,制作一个具有纯文本内容“Hello World”的 Blob 将 go 像new Blob(["Hello World"], {type:"text/plain"}) Your case might be more complicated if you file type isn't plaintext or has a weird encoding, but this is the general idea.如果您的文件类型不是纯文本或具有奇怪的编码,您的情况可能会更复杂,但这是一般的想法。

Another (probably less hack-y) idea would be do the same Blob thing except use JS's native fetch API to get the file contents from your server to the Blob instead of passing it through the template.另一个(可能不那么 hack-y)的想法是做同样的 Blob 事情,除了使用 JS 的本机fetch API 将文件内容从您的服务器获取到 Blob,而不是通过模板传递它。 This is similar to the other answer that suggested saving the file on the server, except instead of providing an a tag you just do the download in pure JS.这类似于建议将文件保存在服务器上的其他答案,除了您只需在纯 JS 中进行下载而不是a标签。

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

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