Почему 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
.