通过jQuery Ajax POST方法下载文件

14
我正在尝试将网页数据导出并将其下载为Excel文件,但即使响应返回成功,下载也不会开始。
$.ajax({
      type: "POST",
      url: _url,
      contentType: 'multipart/form-data;boundary=SzB12x',
      data: json,
    });

响应文本大致如下:
PK�J;Fxl/theme/theme1.xml�YOo�6�[r��n;v��i����#-�kJH:�oC{0X72��mZ���d��u@�(٦b:M��������{|��^�0t@��*"w$�!0I�[�՚n�i��'����iH� g�,��|�J�!���hRh�h��?r&L ���߶S��v@���#��׮�"���}��Жt%�hR�t"������+�����ނ��0K���oy�9OTWywkAͯ���F�� 6*�����[���U���
我认为这是文件,但我无法下载它!
请帮忙一下吗?
谢谢!
6个回答

19

我面临同样的问题并成功解决了。我的用例是这样的。

  • 将JSON数据发布到服务器并接收一个Excel文件。
  • 该Excel文件是即时创建的,并作为响应返回给客户端。

代码:

$("#my-button").on("click", function() {
    // Data to post
    data = {
        ids: [1, 2, 3, 4, 5]
    };

    // Use XMLHttpRequest instead of Jquery $ajax
    xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        var a;
        if (xhttp.readyState === 4 && xhttp.status === 200) {
            // Trick for making downloadable link
            a = document.createElement('a');
            a.href = window.URL.createObjectURL(xhttp.response);
            // Give filename you wish to download
            a.download = "test-file.xls";
            a.style.display = 'none';
            document.body.appendChild(a);
            a.click();
        }
    };
    // Post data to URL which handles post request
    xhttp.open("POST", excelDownloadUrl);
    xhttp.setRequestHeader("Content-Type", "application/json");
    // You should set responseType as blob for binary responses
    xhttp.responseType = 'blob';
    xhttp.send(JSON.stringify(data));
});

以上代码片段只是做了以下几件事情:

  • 使用XMLHttpRequest将一个数组以JSON格式发送到服务器
  • 在获取二进制内容后,我们创建一个可下载的URL并将其附加到不可见“a”链接,然后点击它。

在这里,我们需要在服务器端仔细设置一些东西。我在Python Django HttpResponse中设置了一些标头。如果您使用其他编程语言,则需要相应地设置它们。

# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

我在这里下载xls(excel)文件时,将contentType调整为上面的一个。您需要根据自己的文件类型进行设置。


完美地工作了。供其他人参考,看起来createObjectURL在几乎所有浏览器中都可以使用,但IE8除外。 - issa marie tseng
1
也适用于我,但想知道为什么它不能与jQuery $ajax一起使用。 - mendieta
如果文件很大,也就是说这是一个三步流程,首先服务器发送到Javascript,然后Javascript发送到客户端,我们能否流式传输文件,而不是一次性获取整个文件并下载它? - Guru Vishnu Vardhan Reddy

6
尝试使用隐藏表单来提交请求。
当用户提交HTML表单时,所有由用户输入的数据都会作为GET或POST请求发送到FORM中指定的“ACTION”属性的URL。
 <FORM action="http://www.labnol.org/sendmail.php" method="post">
 ...form contents...
 </FORM>

在上面的例子中,提交表单时会向sendmail.php脚本发出HTTP POST请求。您可以在FORM标签中添加target =“_blank”以在新窗口中处理请求。
但是,如果您想在不将浏览器重定向到另一个页面的情况下,在页面上提交FORM(document.location.href在表单提交时更改),您有两个选择:
选项#1。您可以在HTML页面内创建一个不可见的IFRAME,并将其设置为原始FORM的目标。这将提交表单但不重新加载父窗口。
<FORM action="http://example.com/script.php" 
           method="POST" target="hidden-form">
 ...form contents... 
 </FORM>
<IFRAME style="display:none" name="hidden-form"></IFRAME> 

选项#2:还有一种方法允许您在提交表单之前创建自定义有效载荷。与基于IFRAME的表单提交不同,以下代码会发出标准的表单提交请求,因此您的浏览器位置将更改,并且当前页面将添加到浏览器历史记录中。 信用:Rakesh Pai。

submitFORM('http://example.com/script.php', 'POST',
    {'name':'digital+inspiration', 'age':'100', 'sex','M'});

function submitFORM(path, params, method) {
    method = method || "post"; 

    var form = document.createElement("form");
    form.setAttribute("method", method);
    form.setAttribute("action", path);

    //Move the submit function to another variable
    //so that it doesn't get overwritten.
    form._submit_function_ = form.submit;

    for(var key in params) {
        if(params.hasOwnProperty(key)) {
            var hiddenField = document.createElement("input");
            hiddenField.setAttribute("type", "hidden");
            hiddenField.setAttribute("name", key);
            hiddenField.setAttribute("value", params[key]);

            form.appendChild(hiddenField);
         }
    }

    document.body.appendChild(form);
    form._submit_function_();
}

这个链接中,您可以找到创建隐藏表单并提交的方法。
祝您使用愉快!

4
这里的方法是从https://gist.github.com/DavidMah/3533415直接提取的。
这种方法使用<form>并将数据与键一起附加。如果服务器已经期望将数据作为请求体属性而不是请求体本身,则此方法适用。如果要上传的数据是一个对象,可以迭代该对象的键。如果要上传的数据是一个数组,请修改服务器路由或[添加想法在这里]。

在浏览器中

// Takes a URL, param name, and data string
// Sends to the server... The server can respond with binary data to download
jQuery.download = function(url, key, data) {
    // Build a form
    var form = $('<form></form>').attr('action', url).attr('method', 'post');
    // Add the one key/value
    form.append($("<input></input>").attr('type', 'hidden').attr('name', key).attr('value', data));
    //send request
    form.appendTo('body').submit().remove();
};

在服务器上

# A Tidbit of sinatra code to respond
# Assume 'url' is a set variable
# Assume 'key' is the key of the value used in the javascript

post url do
  data = params[:key]
  puts request.body.read
  headers['Content-Type'] = "application/octet-stream"

  body(data)
end

示例

$.download('/path/resource/', 'data', JSON.stringify(data))

这是我案例中最简单和最好的解决方案。非常感谢。 - Ankur Thakur

0
如果您只想下载一个文件,您不需要使用ajax来完成。实际上,您无法使用ajax下载文件。
您仍然可以通过创建超链接<a href="your_link">Export</a>请求到一个响应content-typeapplication/vnd.ms-excelcontent-dispositionattachment的服务器页面来完成。

你绝对可以使用 AJAX 下载文件。 - Kennet Celeste
@KennetCeleste 这个回答是来自于 2015 年的。你注意到了吗? :) - Cà phê đen

-2
$.ajax({
    type: "POST",
    contentType: "application/json; charset=utf-8",
    url: yoururlpath,
    success: function (response) {
        var file = fileName+".xlsx";
        window.location = "someFilePath?file=" + file;
    }
});

如何处理响应? - jRicardo

-2

您也可以使用 iFrame 来实现此功能。以下是一个示例函数:

// append the data to URL
var requestData = {
    param1 : "value1",
    param2 : "value2",
}

// call the function
downloadFile(<your_URL>, requestData);

function downloadFile(requestURL, data) {
    // "transData" is just a user defined variable to encapsulate "downloadIFrame". It can be named anything as required.
    var downloadIFrame = window.transData.downloadIFrame = window.transData.downloadIFrame || $("#downloadFileiFrame");
    downloadIFrame.attr("src", requestURL + $.param(requestData));
}

// define the iFrame in your HTML and hide it.
<iframe id="downloadFileiFrame" style="display:none;"></iframe>"

1
我需要将JSON数据发送到服务器,iFrame如何实现这一点? - أسامة حمدان
你可以将它附加到URL上。我已经更新了我的答案。 - Pramod Karandikar
正在进行的调用是获取而不是提交。 - Mateen
1
iFrames是一种过时的解决方案。它们存在严重的安全隐患,不应该被使用。 - GoldBishop

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