【问题标题】:Why is ProgressEvent.lengthComputable false?为什么 ProgressEvent.lengthComputable 为假?
【发布时间】:2012-06-23 01:37:23
【问题描述】:

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

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

这是标题:

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 显示数百个事件,其中.loadeds 是最新的,但.lengthComputable 始终为假,.total 始终为零。 self 指的是负责这个XMLHttpRequest 的对象。

【问题讨论】:

  • 我们能看到你的 javascript 代码正在查看这些数据吗?
  • 你试过在非谷歌应用引擎服务器上使用它吗?如果lengthComputable 为假,则 xhr 对象不知道文件有多长。我们在这里使用 GAE,但它的大部分功能都存在大量问题——这不足为奇。
  • 我尝试了另一台服务器,但它给了我分块编码,因此没有 Content-Length。在这种情况下,lengthComputable == false 是有意义的。稍后我将不得不尝试另一个。在我看来,GAE 行为的标题 - 但我和你一样怀疑。

标签: javascript xmlhttprequest


【解决方案1】:

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

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

【讨论】:

  • 如果您要设置被忽略的内容长度标头,您还必须设置响应的缓冲区大小,以便能够容纳您正在发送的字节。否则在 HTTP 1.1 中它将恢复为分块响应。
  • 或者,多年后,当在 Chrome 中使用 gzip'd 内容时,即使设置了正确的标题。 :-(
【解决方案2】:

同时 2017 年,Firefox 的情况还不错,但 Chrome 没有显示 gzip 内容的进度。

这似乎是由于规范曾经不清楚loadedtotal 是指压缩的还是未压缩的内容。 Since 2014 年 6 月 26 日 the XMLHttpRequest specifications 明确他们应该指的是传输(压缩)的内容:

6.1。使用ProgressEvent 接口触发事件

[...] 给定 transmittedlength [...] 触发事件 [...] ProgressEventloaded 属性已初始化到 transmitted,如果 length 不为 0,则 lengthComputable 属性初始化为 true,total 属性初始化为 length

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

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

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

假设 Chrome 的动态解压缩不会改变。

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

【讨论】:

  • 至少:我假设event.loaded的值是解压后的大小(而不是传输的长度)与crbug.com/763700无关...
【解决方案3】:

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

【讨论】:

  • 我也看到req.addEventListener event.lengthComputable 是真的。大型请求似乎是错误的:我尝试的是 10MB JPG。我没有进行广泛的测试,但您可能是对的,req.uploadlengthComputable 更常见。
  • ...但顾名思义:这是从浏览器上传到服务器,而不是从服务器到浏览器下载。
【解决方案4】:

只需在 PHP 或 apache/nginx 中设置

header("Content-Encoding: none");

问题解决了。

【讨论】:

    【解决方案5】:

    在控制器代码中设置 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 + '%';
        }
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-18
      • 2023-04-02
      • 1970-01-01
      • 2018-05-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-01
      相关资源
      最近更新 更多