【问题标题】:AJAX response gives a corrupted compressed (.tgz) fileAJAX 响应提供损坏的压缩 (.tgz) 文件
【发布时间】:2013-11-29 14:08:11
【问题描述】:

我们正在实现一个客户端 Web 应用程序,它专门通过 XMLHttpRequests(和 AJAX 引擎)与服务器通信。

XHR 响应通常是带有一些 XML 的纯文本,但在这种情况下,服务器正在发送 .tgz 文件类型的压缩数据。我们确信服务器发送的数据是正确的,因为如果我们使用 HTTP 命令行客户端(例如 curl),作为响应发送的文件是有效的并且包含预期的数据。

但是,当进行 AJAX 调用并在可下载文件中“散播”响应时,我们获得的文件的大小(大于)与正确的文件不同,并且解压缩器无法识别该文件。它给出了以下错误:

gzip: stdin: not in gzip format
/bin/gtar: Child returned status 1
/bin/gtar: Error is not recoverable: exiting now

我使用的代码如下:

*$.AJAX*.done(function(data){
    window.URL = window.webkitURL || window.URL;
    var contentType = 'application/x-compressed-tar';
    var file = new Blob([data], {type: contentType});
    var a = document.createElement('a'),
    ev = document.createEvent("MouseEvents");
    a.download = "browser_download2.tgz";
    a.href = window.URL.createObjectURL(file);
    ev.initMouseEvent("click", true, false, self, 0, 0, 0, 0, 0,
            false, false, false, false, 0, null);
    a.dispatchEvent(ev);
});

我避免了用于进行 AJAX 调用的参数,但让我们假设这不是问题,因为我正确地收到了答案。我使用这个 contentType 是因为与 curl 获得的显示的内容相同,但我尝试了不同的内容。代码可能看起来有点奇怪,所以我会为你去除光泽:我基本上是在创建一个链接,并附上下载链接和文件名(这是一种肮脏的方式来命名文件)。最后,我实际上是在点击链接。

我比较了正确的 tgz 文件和通过带有十六进制查看器的浏览器获得的文件,我观察到损坏的文件(EF、BF 和 BD,整个文件)中重复出现的模式在正确的文件中不存在.

因此我想到了一些可能的原因:

(a) 浏览器正在添加额外的字符或者可能是响应 标题仍在下载的文件中。

(b) 文件已部分解压缩,因为当我检查时 请求标头我可以声明“接受编码:gzip,放气”; 虽然我不知道浏览器是否(在我的情况下为 Firefox) 自动解压数据。

(c) 我用来 blob 数据的代码不正确;虽然 它在另一个中使用纯/文本文件很好地实现了目标 场合。

编辑

我还为您提供了十六进制检查的链接:

(a) 损坏的文件:http://en.webhex.net/view/278aac05820c34dfbdd2217c03970dd9/0 (b)(大概)正确的文件:http://en.webhex.net/view/4a01894b814c17d2ec71ba49ac48e683

【问题讨论】:

    标签: javascript ajax compression download gunzip


    【解决方案1】:

    我不知道这个帖子是否对某人有帮助,但以防万一我找出问题的原因和可能的解决方案。

    原因

    默认 Javascript 变量以 Unicode/ASCII 格式存储信息;它们没有准备好正确存储二进制数据,这就是为什么人们很容易看到解释错误的字符(这也解释了为什么在十六进制查看器中观察到重复的 EF、BF 等,它们代表 ASCII/Unicode 的错误字符) .

    解决方案

    最后的浏览器版本实现了所谓的类型化数组。它们是 javascript 数组,可以以不同的格式(也是二进制)存储数据。然后,如果指定 XMLHttpRequest 响应为二进制格式,则数据将被正确存储,并且当将其放入文件中时,该文件不会被损坏。查看我使用的代码:

    var xhr = new XMLHttpRequest();
    xhr.open('POST', url, true);
    xhr.responseType = 'arraybuffer';
    

    请注意,关键点是将 responseType 定义为“arraybuffer”。注意到我决定不再将 Jquery 用于 AJAX 可能也很有趣。它很好地实现了这个功能,我为解析 Jquery 所做的所有尝试都是徒劳的(在其他地方描述的 overrideMimeType 在我的情况下不起作用)。相反,旧的普通 XMLHttRquest 工作得很好。

    【讨论】:

    • 2015 年依然有用!
    • 我可以确认 jQuery 3.3.1 在 2019 年仍然是这种情况,并且这个答案仍然有效。