为什么ProgressEvent.lengthComputable为false?

时间:2012-06-20 20:27:30

标签: javascript xmlhttprequest

我在Google Chrome,Safari和Firefox中使用XMLHttpRequest加载JSON文件。在所有三个浏览器中,我都会收到ProgressEvent个正确显示.loaded属性的内容。但是.lengthComputable属性为false,.total属性为零。我已经检查过Content-Length HTTP标头正在发送并且是正确的 - 它是。响应是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显示了数百个最新.loaded s的事件,但.lengthComputable始终为false,.total始终为零。 self指的是负责此XMLHttpRequest的对象。

5 个答案:

答案 0 :(得分:17)

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

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

答案 1 :(得分:5)

与2017年同期,Firefox的情况很好,但Chrome没有显示gzip内容的进展。

这似乎是由于loadedtotal引用压缩或未压缩内容后一直不清楚的规范引起的。 Since 2014年6月26日the XMLHttpRequest specifications明确指出他们应该参考传输(压缩)的内容:

  

6.1。使用ProgressEvent界面

发射事件      

[...] 传输长度 [...]用[{1] ProgressEvent激活事件[...] loaded初始化为传输的属性,如果 length 不为0,则lengthComputable属性初始化为true且total属性初始化为< EM>长度

然而,2015年Chromium错误报告"XHR's progress events should handle gzipped content"解释说情况有所不同,并指出:

  

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

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

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

我不知道Chrome为Content-Encoding的其他值做了什么。

答案 2 :(得分:4)

使用 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);

答案 3 :(得分:0)

只需在PHP或apache / nginx中设置

即可
header("Content-Encoding: none");

问题解决了。

答案 4 :(得分: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 + '%';
    }
};