使用Blob从Ajax结果下载文件

26

我使用这段代码从服务器下载Excel文件。

$.ajax({
    headers: CLIENT.authorize(),
    url: '/server/url',
    type: 'POST',
    contentType: "application/json; charset=utf-8",
    data: JSON.stringify(jsonData),
    success: function (data) {
        alert('Data size: ' + data.length);
        var blob = new Blob([data], { type: "application/vnd.ms-excel" });
        alert('BLOB SIZE: ' + data.length);
        var URL = window.URL || window.webkitURL;
        var downloadUrl = URL.createObjectURL(blob);
        document.location = downloadUrl;
    },
});

我遇到的问题是,尽管数据和blob大小相同,但一旦document.location被分配,我就会被提示下载几乎两倍大的Excel文件。当我尝试打开它时,Excel会抱怨文件格式错误,并且打开文件包含很多垃圾,即使所需的文本仍然存在。

有什么想法是什么原因导致这种情况,并如何避免?


我认为这不是一个好主意。为什么不让服务器将文件推送到浏览器进行正常的下载流呢?除非你有非常特定的原因要这样做,比如某种代理或在线分析,否则这似乎非常不自然,只会使浏览器内存受损。 - Piotr Kula
尝试将内容类型设置为 arrayBuffer - levi
@ppumkin,不幸的是我需要满足要求。我不能将文件存储在服务器上,而且我需要传递授权头来获取文件。 - SMGhost
@levi,您是指在创建Blob时指定JSON的内容类型吗? - SMGhost
是的。 - levi
1个回答

47

我使用 AJAX 2 解决了这个问题,它本身支持二进制流。除非您明确将所有内容进行 base64 编码,否则不能使用 jQuery。

工作代码如下:

var xhr = new XMLHttpRequest();
xhr.open('POST', '/le/url', true);
xhr.responseType = 'blob';
$.each(SERVER.authorization(), function(k, v) {
    xhr.setRequestHeader(k, v);
});
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
xhr.onload = function(e) {
    preloader.modal('hide');
    if (this.status == 200) {
        var blob = new Blob([this.response], {type: 'application/vnd.ms-excel'});
        var downloadUrl = URL.createObjectURL(blob);
        var a = document.createElement("a");
        a.href = downloadUrl;
        a.download = "data.xls";
        document.body.appendChild(a);
        a.click();
    } else {
        alert('Unable to download excel.')
    }
};
xhr.send(JSON.stringify(jsonData));

希望这能帮到你。


1
适用于 Chrome/FF。对于 IE,请使用 msSaveOrOpenBlob。https://dev59.com/pmAg5IYBdhLWcg3wE3jQ - user2827377
2
这个可以用.. 我在Chrome上测试过了.. 在Safari上不行 :( - vasanth
1
+1 对于 [this.response],如何获取 blob 实际上,我尝试了这样的方式 window.URL.createObjectURL(xhttp.response)。 - Andi AR
谢谢!令人震惊的是这并不是很简单。看起来应该是常见的事情,但我花了很长时间才找到它。 - Stephen McCormick
请注意,要在Firefox上使其正常工作,您必须将以下内容添加到body中:在答案中:document.body.appendChild(a)...在我的代码中,我放置了document.body && document.body.appendChild(link); - Zachary Ryan Smith
对于Firefox和可能的Safari浏览器,链接必须添加到body中,并可以隐藏: |document.body.appendChild(link);link.setAttribute('type', 'hidden'); - gabrielgeo

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