Почему ProgressEvent.lengthComputable false?

Я загружаю файл JSON с использованием XMLHttpRequest в Google Chrome, Safari и Firefox. Во всех трех браузерах я получаю ProgressEvent, которые правильно отображают свойство .loaded. Однако свойство .lengthComputable является ложным, а свойство .total равно нулю. Я проверил, что HTTP-заголовок Content-Length отправляется и является правильным - это так. Ответ будет gzip-encoded, но 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, но .lengthComputable всегда false, а .total всегда равно нулю. self относится к объекту, ответственному за этот XMLHttpRequest.

Ответы

Ответ 1

Если lengthComputable является ложным в XMLHttpRequestProgressEvent, это означает, что сервер никогда не отправлял заголовок Content-Length в ответ.

Если вы используете nginx в качестве прокси-сервера, это может быть преступником, особенно если он не передает заголовок Content-Length с восходящего сервера через прокси-сервер в браузер.

Ответ 2

используйте 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

В то же время 2017, все в порядке в Firefox, но Chrome не показывает прогресс для содержимого gzip'd.

Это, по-видимому, вызвано спецификациями, когда неясно, относятся ли loaded и total к сжатому или несжатому контенту. Начиная с 26 июня 2014 г. спецификации XMLHttpRequest ясно они должны ссылаться на переданный (сжатый) контент:

6.1. События обхода с использованием интерфейса ProgressEvent

[...] передается и длина [...] запускает событие [...] ProgressEvent, с атрибутом loaded, инициализированным для передачи, и если длина не равна 0, с lengthComputable, инициализированный атрибутом true, и атрибутом total, инициализированным длиной.

Однако, отчет об ошибке Chromium 2015 "События прогресса XHR должны обрабатывать содержимое gzipped" объясняет, что все было по-другому, и говорится:

при кодировании total остается равным 0 и lengthComputable не установлен

Само событие все еще запущено, а event.loaded по-прежнему заполняется. Но Chrome распаковывает gzip'd-контент на лету и (сегодня) устанавливает loaded в результирующую декомпрессированную длину, а не на переданную длину. Это нельзя сравнить со значением заголовка Content-Length, так как длина сжатого содержимого, поэтому loaded станет больше длины содержимого.

В лучшем случае можно предположить некоторый коэффициент сжатия для сравнения loaded с Content-Length или сделать сервер добавлением некоторого настраиваемого заголовка, чтобы обеспечить исходную длину или истинный коэффициент сжатия, предполагая, что Chrome на лету декомпрессии не будет изменение.

Я не знаю, что делает Chrome для других значений Content-Encoding.