通过 AJAX 请求下载 CSV 文件作为响应

14

我有一个名为"/downloadUserAction"的端点,用于收集数据并下载CSV文件。我的挑战是,当我调用端点时使用按钮的点击功能时,文件不会被下载,但当我直接在浏览器中访问端点时,它会下载。

经过研究,我发现不能使用AJAX来下载文件。这很有道理,因为当我点击按钮时,我看到端点被命中,并且文件内容在网络选项卡中传递,但没有文件被下载到客户端。

这是我在Javascript方面所做的全部工作,只是在我的页面上使用数据表按钮插件功能来调用我的端点。

$(document).ready(function () {
    var table = $("#userActivity").on('init.dt', function() {
            }).DataTable({
                dom: 'Blfrtip',
                buttons: [
                          {
                            extend: 'csvHtml5',
                            text: 'NLP Search Download',
                            action: function ( e, dt, node, config ) {
                                $.ajax({
                                    url : window.location + "/downloadUserAction?draw=3&search%5Bvalue%5D=NLP_SEARCH&order%5B0%5D%5Bcolumn%5D=6&order%5B0%5D%5Bdir%5D=desc"                        
                                });
                            }
                          }
                      ],

是否有其他调用我的端点的方法可以强制客户页面下载?

附注:我的数据表正在使用服务器端处理,否则我将使用datatables csv导出按钮。

3个回答

38
在现代浏览器中,您可以通过创建一个包含文件内容的 Blob 对象(在您的情况下是通过 Ajax 接收的),为其创建 URL,并使用 download 属性来提示下载。
const saveData = (function () {
    const a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    return function (data, fileName) {
        const blob = new Blob([data], {type: "octet/stream"}),
            url = window.URL.createObjectURL(blob);
        a.href = url;
        a.download = fileName;
        a.click();
        window.URL.revokeObjectURL(url);
    };
}());

const data = 'a,b,c\n5,6,7',
    fileName = "my-csv.csv";

saveData(data, fileName);

JSFiddle

如果您的Ajax端点URL具有适当的标头(甚至可能不需要,只要使用download属性),您可以放弃Blob和Ajax,并仅使用下载属性将URL添加到链接中。根据@pritesh的代码进行调整,

const saveData = (function () {
    const a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    return function (url, fileName) {
        a.href = url;
        a.download = fileName;
        a.click();
    };
}());

const url = 'http://www.sample-videos.com/csv/Sample-Spreadsheet-10-rows.csv', // Replace with your own URL: window.location + "/downloadUserAction?draw=3&search%5Bvalue%5D=NLP_SEARCH&order%5B0%5D%5Bcolumn%5D=6&order%5B0%5D%5Bdir%5D=desc"
    fileName = "my-csv.csv";

saveData(url, fileName);

JSFiddle

可以翻译为:

{{链接1:JSFiddle}}


点击后应该使用 a.remove() 删除锚点。 - adi518
我构建了闭包,因为我认为在内存中只有一个链接比每次创建和销毁一个链接更好。 - Brett Zamir

1
即使这是一个老问题,我仍想发布我想出的答案,因为我遇到了同样的问题。
在客户端上,可以使用FileSaver.js库直接保存blob而不是创建链接并调用click方法的替代方法。
添加FileSaver.js后,您只需使用saveAS(blob, filename)来启动下载即可。
以下示例用于处理Flask服务器send_file(file, as_attachment=True, attachment_filename='export.csv', mimetype='text/csv')响应。
function downloadCsv() {
  jQuery.ajax({
    async: true,
    url: URL,
    timeout: 5000,
    type: "post",
    xhrFields: {
      responseType: "blob", // to avoid binary data being mangled on charset conversion
    },
    data: postRequestdata,
    error: function (e) {
      console.error(e);
    },
    // in jQuery 1.4+ when the success callback its executed three arguments are passed (success(data, textStatus, XMLHttpRequest))
    success: function (blob, status, xhr) {
      // check for a filename
      var filename = "";
      var disposition = xhr.getResponseHeader("Content-Disposition");
      if(disposition && disposition.indexOf("filename") !== -1){
        splitted = disposition.split("filename=");
        filename = splitted[splitted.length-1];
        saveAs(blob, filename);
      }
    },
  });
}

0

由于在直接从浏览器访问端点时会下载它,因此您可以在按钮内设置一个锚标签,并将锚标签的href属性设置为指向csv文件的url。我在这里创建了一个示例:

<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
</head>
<body>
    <button>
        <a href="http://www.sample-videos.com/csv/Sample-Spreadsheet-10-rows.csv">Click me</a>
    </button>
</body>
</html>

如果您在浏览器中尝试此代码,则单击后会出现一个对话框以保存文件。
在您的情况下,您需要用自己的样本视频URL替换它。

window.location + "/downloadUserAction?draw=3&search%5Bvalue%5D=NLP_SEARCH&order%5B0%5D%5Bcolumn%5D=6&order%5B0%5D%5Bdir%5D=desc"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接