2012-06-20 448 views
15

我在Google Chrome,Safari和Firefox中使用XMLHttpRequest加載JSON文件。在所有三種瀏覽器中,我都收到ProgressEvent,它們正確顯示了.loaded屬性。但.lengthComputable屬性爲false,.total屬性爲零。我檢查了Content-Length HTTP頭正在發送並且是正確的 - 確實如此。響應正在進行gzip編碼,但Content-length正確顯示了編碼長度(解壓縮之前)。爲什麼ProgressEvent.lengthComputable爲false?

爲什麼總長度在我的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 

注:該文件正在通過谷歌的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永遠是假的,.total始終爲零。 self是指負責此XMLHttpRequest的對象。

+1

我們可以看到您正在查看此數據的JavaScript代碼嗎? – tkone

+0

我已經添加了。 –

+1

您是否嘗試過在非Google應用引擎服務器上使用此功能?如果'lengthComputable'爲false,那麼xhr對象不知道文件有多長。我們在這裏使用GAE,並且在其大部分功能方面存在大量問題 - 這並不令人驚訝。 – tkone

回答

14

如果XMLHttpRequestProgressEvent中的lengthComputable爲false,則表示服務器從未在響應中發送過Content-Length標頭。

如果您使用nginx作爲代理服務器,這可能是罪魁禍首,特別是如果它沒有通過代理服務器將上傳服務器的Content-Length頭傳遞給瀏覽器。

+0

如果要設置被忽略的內容長度標頭,還必須設置緩衝區大小你的迴應能夠保存你發送的字節。否則,在HTTP 1.1中,它將恢復分塊響應。 –

+0

或許多年後,在Chrome中使用gzip'd內容時,即使設置了正確的標題。 :-( – Arjan

1

使用上傳

req.addEventListener req.upload.addEventListener event.lengthComputable將永遠是假的

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); 
+0

我看到'req.addEventListener'' event.lengthComputable'也是如此。對於大的請求似乎是錯誤的:我試過的是一個10MB的JPG,我沒有做過廣泛的測試,但是你可能是正確的,'req.upload'的'lengthComputable'的確是更經常的。 – trysis

+1

...但是正如名字暗示的那樣:這是從瀏覽器到服務器的上傳,而不是從服務器到瀏覽器的下載 – Arjan

1

同時2017年,事情是在Firefox罰款,但瀏覽器不顯示gzip內容的進度。

如果loadedtotal指的是壓縮或未壓縮的內容,這似乎是由規範一旦不清楚引起的。 Since 2014年6月26日the XMLHttpRequest specifications明確他們應參照傳輸(壓縮的)內容:

6.1。使用ProgressEvent接口點火事件

[...]給出發送長度 [...]觸發事件[...] ProgressEvent,與loaded屬性初始化爲發送,並且如果長度不是0,其中lengthComputable屬性初始化爲true,total屬性初始化爲長度

然而,2015年的鉻bug報告"XHR's progress events should handle gzipped content"解釋說,事情是不同的,並指出:

編碼時,total停留在0和lengthComputable沒有設置

事件本身仍然被解僱,並且event.loaded仍然填充。但Chrome正在動態解壓gzip'd內容,並且(今天)將loaded設置爲所得的解壓縮長度,而不是傳輸的長度。這不能與Content-Length標題的值相比較,因爲這是壓縮的內容的長度,所以loaded將變得大於內容長度。

充其量可以假設一些壓縮係數比較loadedContent-Length,或使服務器添加一些自定義頭,可以提供原始長度或真壓縮因子,假設Chrome的上即時解壓縮不會改變。

我不知道Chrome爲Content-Encoding的其他值做了些什麼。

+0

至少:我假設'event.loaded'是解壓縮大小(而不是傳輸長度)的值與https://crbug.com/763700無關。 – Arjan