18

我在 Google Chrome、Safari 和 Firefox 中使用 XMLHttpRequest 加载 JSON 文件。在所有三个浏览器中,我都收到ProgressEvent了正确显示.loaded属性的 s。但是该.lengthComputable属性为假且该.total属性为零。我检查了Content-LengthHTTP 标头是否正在发送并且是正确的 - 是的。响应是 gzip 编码的,但Content-length正确显示了编码长度(解压缩之前)。

为什么我ProgressEvent的 s 中没有总长度?

以下是标题:

HTTP/1.1 200 OK
ETag: "hKXdZA"
Date: Wed, 20 Jun 2012 20:17:17 GMT
Expires: Wed, 20 Jun 2012 20:17:17 GMT
Cache-Control: private, max-age=3600
X-AppEngine-Estimated-CPM-US-Dollars: $0.000108
X-AppEngine-Resource-Usage: ms=2 cpu_ms=0 api_cpu_ms=0
Content-Type: application/json
Content-Encoding: gzip
Server: Google Frontend
Content-Length: 621606

注意:该文件是通过 Google App Engine 提供的。

这是JavaScript:

var req;
if (window.XMLHttpRequest){
    req = new XMLHttpRequest();
    if(req.overrideMimeType){
        req.overrideMimeType( "text/json" );
    }
}else{
    req = new ActiveXObject('Microsoft.XMLHTTP');
}

// Listen for progress events
req.addEventListener("progress", function (event) {
    console.log(event, event.lengthComputable, event.total);
    if (event.lengthComputable) {
        self.progress = event.loaded / event.total;
    } else if (this.explicitTotal) {
        self.progress = Math.min(1, event.loaded / self.explicitTotal);
    } else {
        self.progress = 0;
    }
    self.dispatchEvent(Breel.Asset.ON_PROGRESS);
}, false);

req.open('GET', this.url);

注意:该console.log代码中的 s 显示了数百个具有最新.loadeds 的事件,但.lengthComputable始终为假且.total始终为零。self指负责 this 的对象XMLHttpRequest

4

5 回答 5

19

如果 XMLHttpRequestProgressEvent 中的 lengthComputable 为 false,则意味着服务器从未在响应中发送 Content-Length 标头。

如果您使用 nginx 作为代理服务器,这可能是罪魁祸首,特别是如果它没有将上游服务器的 Content-Length 标头通过代理服务器传递到浏览器。

于 2012-08-07T15:12:12.687 回答
12

与此同时,2017 年,Firefox 一切正常,但 Chrome 并未显示 gzip 内容的进度。

这似乎是由于规范曾经不清楚是指压缩还是未压缩的内容loaded2014 年6 月 26 日以来, XMLHttpRequest 规范明确表示它们应该引用传输(压缩)的内容:total

6.1。ProgressEvent使用接口触发事件

[...] 给定传输长度[...] 触发事件 [...] ProgressEventloaded属性初始化为传输,如果长度不为 0 ,则lengthComputable属性初始化为 true ,total属性初始化为length

但是,2015 Chromium 错误报告“XHR 的进度事件应该处理 gzip 压缩的内容”解释说情况有所不同,并指出:

编码时,total保持为 0lengthComputable且未设置

事件本身仍然被触发,并且event.loaded仍然被填充。但是 Chrome 正在动态解压缩 gzip 的内容,并且(今天)设置loaded为解压缩后的长度,而不是传输的长度。这不能与Content-Length标头的值进行比较,因为这是压缩内容的长度,因此loaded会大于内容长度。

充其量可以假设一些压缩因子进行比较loadedContent-Length或者让服务器添加一些自定义标头以提供原始长度或真正的压缩因子,假设 Chrome 的动态解压缩不会改变。

我不知道 Chrome 对Content-Encoding.

于 2017-11-14T13:31:01.513 回答
7

使用req.upload.addEventListener进行上传

req.addEventListener event.lengthComputable 将始终为 false

req.upload.addEventListener("progress", function (event) {
    console.log(event, event.lengthComputable, event.total);
    if (event.lengthComputable) {
        self.progress = event.loaded / event.total;
    } else if (this.explicitTotal) {
        self.progress = Math.min(1, event.loaded / self.explicitTotal);
    } else {
        self.progress = 0;
    }
    self.dispatchEvent(Breel.Asset.ON_PROGRESS);
}, false);
于 2017-03-11T11:18:45.053 回答
2

只需在 PHP 或 apache/nginx 中设置

header("Content-Encoding: none");

问题解决了。

于 2018-03-30T20:12:37.650 回答
0

在控制器代码中设置 Content-Length。这将设置event.lengthComputable.

var fileSize = new FileInfo(filePathAbs).Length;
context.Response.AddHeader("Content-Length", fileSize.ToString());
context.Response.AddHeader("content-disposition", "inline;filename=" + fileName);

var req = new XMLHttpRequest();

req.open('GET', '/EReader/GetDocument?p=@Model.EncodedTempFilePath');
req.responseType = "arraybuffer";
req.onprogress = function (event) { 
    if (event.lengthComputable) {
        percentage = Math.floor(event.loaded * 100 / event.total); // give the percentage
        var elem = document.getElementById("bar");
        elem.style.width = percentage + '%';
    }
};
于 2018-06-25T15:51:52.847 回答