打开"另存为" /"下载"使用JavaScript对话框下载动态创建的文件

时间:2016-03-04 20:25:35

标签: javascript jquery flask

我有一个NGINX + Flask + KnockoutJS单页应用程序,我想创建一个下载按钮,允许用户下载他/她可视化的数据并操纵客户端而无需重新加载整页 有许多纯JavaScript解决方案(如download.js),但它们都不与所有主流浏览器完全兼容(例如Safari)。

基本上我想拥有的是:

  1. 该应用向用户显示一个表格
  2. 用户按下载按钮
  3. JavaScript将数据发送到服务器端端点
  4. 服务器根据客户端发送的数据动态生成文件
  5. 浏览器打开下载/另存为对话框
  6. 有可能吗?

3 个答案:

答案 0 :(得分:2)

  

有可能吗?

当然。我们举个例子:

  
      
  1. 该应用向用户显示一个表格
  2.   

你的意思是,使用<table>标签:

<table>
    <tr>
        <td>Foo bar</td>
        <td>123</td>
        <td>
            <form action="/download/foo/bar/123" method="post">
                <button type="submit" value="Download foo bar 123" />
            </form>
        </td>
    </tr>

    ... here come some other rows of the table ...
</table>
  
      
  1. 用户按下载按钮
  2.   
好的,这个很明显。用户通过单击表格所需行上的相应提交按钮来提交表单。

  
      
  1. JavaScript将数据发送到服务器端端点
  2.   

为什么在拥有标准HTML表单时可以关注javascript,这些表单可以纯粹以浏览器无关的方式向服务器提交数据,如第1点所示。如果你真的关心某些javascript,你可以随时订阅我之前展示的onsubmit <form>动作,并使用javascript将必要的数据作为隐藏字段注入DOM。

  
      
  1. 服务器根据客户端发送的数据动态生成文件
  2.   

是的,这就像标准的HTTP协议。服务器只需处理/download/foo/bar/123端点,只需将文件作为附件发送:

HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 29
Content-Disposition: attachment; filename=foobar123.bin

HERE COMES THE BINARY CONTENT
  
      
  1. 浏览器打开下载/另存为对话框
  2.   

这正是任何浏览器在处理来自服务器的先前显示的HTTP响应时会做的事情。

结论:HTTP协议和标准HTML表单已经为您提供了满足您需求的必要工具。如果你想要一点额外的好感,只需在使用javascript提交时增强HTML表单,以便将任何必填字段作为隐藏的输入元素附加到您想要发送到服务器的位置。然后将其留给浏览器来处理下载。

答案 1 :(得分:0)

浏览器将根据自己的下载设置显示“另存为”对话框。是否下载文件取决于操作系统是否具有处理打开文件的程序。您可以通过使用下载属性创建锚点来强制下载(覆盖打开文件)。

答案 2 :(得分:0)

在我意识到,也许我的问题并非100%明确。无论如何,我想分享我想出的解决方案。我在Flask应用程序中创建了两个结束点:

第一个通过AJAX POST从客户端获取数据并将它们临时存储在Redis中(我已经将Redis实例激活用于缓存)并为该文件生成UUID。

@mod.route("/create-csv", methods=['POST'])
def create_csv():
    csv_string = request.form.get('csv')
    file_id = str(uuid())
    rstore.setex(file_id, 60, csv_string)
    return jsonify({}), 202, {'Location': url_for('api.download',
                                              file_id=file_id,
                                              _external=True, 
                                              _scheme='https')}

第二个端点只是将文件发送到具有相应标头的客户端。

@mod.route("/download/<file_id>", methods=['GET'])
def download(file_id):
    file_content = rstore.get(file_id)
    response = make_response(file_content)
    response.headers["Content-Disposition"] = "attachment; filename=keywords.csv"
    response.headers['Content-Type'] = "application/octet-stream"
    return response

在客户端站点上,我有以下JavaScript代码:

  self.save = function(csvdata) {
    $.post( "/api/create-csv", csvdata, function(data, status, response){

      var file_url = response.getResponseHeader('Location');
      window.location.assign(file_url);

    });
  }

因此,当成功发送POST请求时,我只需为当前URL分配文件下载的URL。