問題描述
我之前看到過很多與此類似的問題,但我還沒有找到一個準確描述我當前問題的問題,所以這里是:
我有一個通過 AJAX 加載大型(0.5 到 10 MB 之間)JSON 文檔的頁面,以便客戶端代碼可以處理它.加載文件后,我不會遇到任何我沒想到的問題.但是,下載需要很長時間,所以我嘗試利用
這些是相關的 request 標頭,表明請求是 AJAX 并且 Accept-Encoding
設置正確:
GET/dashboard/reports/ajax/load HTTP/1.1連接:保持活動緩存控制:無緩存Pragma:無緩存接受:應用程序/json、文本/javascript、*/*;q=0.01X-Requested-With: XMLHttpRequest用戶代理:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.99 Safari/537.22接受編碼:gzip、deflate、sdch接受語言:en-US,en;q=0.8接受字符集:ISO-8859-1,utf-8;q=0.7,*;q=0.3
這些是相關的 response 標頭,表明 Content-Length
和 Content-Type
設置正確:
HTTP/1.1 200 OK緩存控制:無存儲、無緩存、必須重新驗證、后檢查 = 0、預檢查 = 0內容編碼:放氣內容類型:應用程序/json日期:格林威治標準時間 2013 年 2 月 26 日星期二 18:59:07到期:1981 年 11 月 19 日星期四 08:52:00 GMTP3P: CP="CAO PSA OUR"Pragma:無緩存服務器:Apache/2.2.8 (Unix) mod_ssl/2.2.8 OpenSSL/0.9.8g PHP/5.4.7X-Powered-By: PHP/5.4.7內容長度:223879連接:保持活動
對于它的價值,我在標準 (http) 和安全 (https) 連接上都試過了,沒有任何區別:內容在瀏覽器中加載正常,但沒有被 Progress API 處理.
<小時>根據 Adam 的建議,我嘗試將服務器端切換為 gzip 編碼,但沒有成功或更改.以下是相關的響應標頭:
HTTP/1.1 200 OK緩存控制:無存儲、無緩存、必須重新驗證、后檢查 = 0、預檢查 = 0內容編碼:gzip內容類型:應用程序/json日期:格林威治標準時間 2013 年 3 月 4 日星期一 22:33:19到期:1981 年 11 月 19 日星期四 08:52:00 GMTP3P: CP="CAO PSA OUR"Pragma:無緩存服務器:Apache/2.2.8 (Unix) mod_ssl/2.2.8 OpenSSL/0.9.8g PHP/5.4.7X-Powered-By: PHP/5.4.7內容長度:28250連接:保持活動
重復一遍:內容正在被正確下載和解碼,這只是我遇到問題的進度 API.
<小時>根據 Bertrand 的請求,請求如下:
$.ajax({url: '<截斷的網址>',數據: {},成功:onDone,數據類型:'json',緩存:真,進度:onProgress ||功能(){}});
這是我正在使用的 onProgress
事件處理程序(這不是太瘋狂):
函數(jqXHR, evt){//是的,我知道這有時會產生無窮大var pct = 100 * evt.position/evt.total;//只是一個更新一些樣式和javascript的方法更新進度(pct);});
我無法解決在壓縮內容本身上使用 onProgress
的問題,但我想出了這個半簡單的解決方法.簡而言之:在發送GET
請求的同時向服務器發送HEAD
請求,一旦有足夠的信息就渲染進度條這樣做.
函數加載器(onDone, onProgress, url, data){//onDone = 成功下載時運行的事件處理程序//onProgress = 在下載期間運行的事件處理程序//url = 要加載的 url//data = 與 AJAX 請求一起發送的額外參數var content_length = null;self.meta_xhr = $.ajax({網址:網址,數據:數據,數據類型:'json',類型:'頭',成功:函數(數據、狀態、jqXHR){content_length = jqXHR.getResponseHeader("X-Content-Length");}});self.xhr = $.ajax({網址:網址,數據:數據,成功:onDone,數據類型:'json',進度:函數(jqXHR,evt){var pct = 0;如果(evt.lengthComputable){pct = 100 * evt.position/evt.total;}否則 if (self.content_length != null){pct = 100 * evt.position/self.content_length;}onProgress(pct);}});}
然后使用它:
loader(函數(響應){console.log("內容已加載!立即執行.");},函數(pct){console.log("內容為" + pct + "%已加載.");},'<這里的網址>', {});
<小時>
在服務器端,在 GET
和 HEAD
請求上設置 X-Content-Length
標頭(應該代表未壓縮內容長度),并中止發送 HEAD
請求中的內容.
在 PHP 中,設置標頭如下所示:
header("X-Content-Length: ".strlen($payload));
如果是 HEAD
請求,則中止發送內容:
if ($_SERVER['REQUEST_METHOD'] == "HEAD"){出口;}
<小時>
下面是實際效果:
HEAD
在下面的截圖中需要這么長時間的原因是服務器仍然需要解析文件才能知道它有多長,但這是我絕對可以改進的,而且絕對是比原來有所改進.
I've seen a bunch of similar questions to this get asked before, but I haven't found one that describes my current problem exactly, so here goes:
I have a page which loads a large (between 0.5 and 10 MB) JSON document via AJAX so that the client-side code can process it. Once the file is loaded, I don't have any problems that I don't expect. However, it takes a long time to download, so I tried leveraging the XHR Progress API to render a progress bar to indicate to the user that the document is loading. This worked well.
Then, in an effort to speed things up, I tried compressing the output on the server side via gzip and deflate. This worked too, with tremendous gains, however, my progress bar stopped working.
I've looked into the issue for a while and found that if a proper Content-Length
header isn't sent with the requested AJAX resource, the onProgress
event handler cannot function as intended because it doesn't know how far along in the download it is. When this happens, a property called lengthComputable
is set to false
on the event object.
This made sense, so I tried setting the header explicitly with both the uncompressed and the compressed length of the output. I can verify that the header is being sent, and I can verify that my browser knows how to decompress the content. But the onProgress
handler still reports lengthComputable = false
.
So my question is: is there a way to gzipped/deflated content with the AJAX Progress API? And if so, what am I doing wrong right now?
This is how the resource appears in the Chrome Network panel, showing that compression is working:
These are the relevant request headers, showing that the request is AJAX and that Accept-Encoding
is set properly:
GET /dashboard/reports/ajax/load HTTP/1.1
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.99 Safari/537.22
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
These are the relevant response headers, showing that the Content-Length
and Content-Type
are being set correctly:
HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Encoding: deflate
Content-Type: application/json
Date: Tue, 26 Feb 2013 18:59:07 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
P3P: CP="CAO PSA OUR"
Pragma: no-cache
Server: Apache/2.2.8 (Unix) mod_ssl/2.2.8 OpenSSL/0.9.8g PHP/5.4.7
X-Powered-By: PHP/5.4.7
Content-Length: 223879
Connection: keep-alive
For what it's worth, I've tried this on both a standard (http) and secure (https) connection, with no differences: the content loads fine in the browser, but isn't processed by the Progress API.
Per Adam's suggestion, I tried switching the server side to gzip encoding with no success or change. Here are the relevant response headers:
HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Encoding: gzip
Content-Type: application/json
Date: Mon, 04 Mar 2013 22:33:19 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
P3P: CP="CAO PSA OUR"
Pragma: no-cache
Server: Apache/2.2.8 (Unix) mod_ssl/2.2.8 OpenSSL/0.9.8g PHP/5.4.7
X-Powered-By: PHP/5.4.7
Content-Length: 28250
Connection: keep-alive
Just to repeat: the content is being downloaded and decoded properly, it's just the progress API that I'm having trouble with.
Per Bertrand's request, here's the request:
$.ajax({
url: '<url snipped>',
data: {},
success: onDone,
dataType: 'json',
cache: true,
progress: onProgress || function(){}
});
And here's the onProgress
event handler I'm using (it's not too crazy):
function(jqXHR, evt)
{
// yes, I know this generates Infinity sometimes
var pct = 100 * evt.position / evt.total;
// just a method that updates some styles and javascript
updateProgress(pct);
});
I wasn't able to solve the issue of using onProgress
on the compressed content itself, but I came up with this semi-simple workaround. In a nutshell: send a HEAD
request to the server at the same time as a GET
request, and render the progress bar once there's enough information to do so.
function loader(onDone, onProgress, url, data)
{
// onDone = event handler to run on successful download
// onProgress = event handler to run during a download
// url = url to load
// data = extra parameters to be sent with the AJAX request
var content_length = null;
self.meta_xhr = $.ajax({
url: url,
data: data,
dataType: 'json',
type: 'HEAD',
success: function(data, status, jqXHR)
{
content_length = jqXHR.getResponseHeader("X-Content-Length");
}
});
self.xhr = $.ajax({
url: url,
data: data,
success: onDone,
dataType: 'json',
progress: function(jqXHR, evt)
{
var pct = 0;
if (evt.lengthComputable)
{
pct = 100 * evt.position / evt.total;
}
else if (self.content_length != null)
{
pct = 100 * evt.position / self.content_length;
}
onProgress(pct);
}
});
}
And then to use it:
loader(function(response)
{
console.log("Content loaded! do stuff now.");
},
function(pct)
{
console.log("The content is " + pct + "% loaded.");
},
'<url here>', {});
On the server side, set the X-Content-Length
header on both the GET
and the HEAD
requests (which should represent the uncompressed content length), and abort sending the content on the HEAD
request.
In PHP, setting the header looks like:
header("X-Content-Length: ".strlen($payload));
And then abort sending the content if it's a HEAD
request:
if ($_SERVER['REQUEST_METHOD'] == "HEAD")
{
exit;
}
Here's what it looks like in action:
The reason the HEAD
takes so long in the below screenshot is because the server still has to parse the file to know how long it is, but that's something I can definitely improve on, and it's definitely an improvement from where it was.
這篇關于如何通過 XHR onProgress 函數使用壓縮/壓縮的內容?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!