如何使用XHR onProgress函数使用deflated / gzipped内容? [英] How can I use deflated/gzipped content with an XHR onProgress function?

查看:681
本文介绍了如何使用XHR onProgress函数使用deflated / gzipped内容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看到过一些类似的问题,但我没有找到一个描述我的当前问题,所以这里:



我有一个页面,通过AJAX加载一个大(0.5到10 MB)JSON文档,以便客户端代码可以处理它。一旦文件加载,我没有任何问题,我不期望。但是,下载需要很长时间,因此我尝试利用 XHR Progress API < a>以呈现进度条以向用户指示文档正在加载。这工作得很好。



然后,为了加快速度,我尝试通过gzip和deflate压缩服务器端的输出。但是,我的进度条停止了工作。



我已经研究了一段时间,发现如果一个正确的 Content-Length 头不与请求的AJAX资源一起发送,因为 onProgress 事件处理程序不能正常工作,因为它不知道在下载有多远。发生这种情况时,对事件对象设置 lengthComputable 的属性 false



这是有意义的,所以我试图明确地设置与输出的未压缩和压缩长度的头。我可以验证标头是否正在发送,我可以验证我的浏览器知道如何解压缩内容。但是 onProgress 处理程序仍然报告 lengthComputable = false



所以我的问题是:有一个方法使用AJAX Progress API gzip / deflated内容如果是,我现在做错了什么?






这是资源在Chrome网络面板中的显示方式,表明压缩是有效的:



img src =http://dl.dropbox.com/u/1766969/Screenshots/m_b4.pngalt =网络面板>



这些是相关的请求标头,显示请求是AJAX,并且 Accept-Encoding 设置正确:



连接:keep-alive
缓存控制:no-缓存
Pragma:no-cache
接受:application / json,text / javascript,* / *; q = 0.01
X请求使用:XMLHttpRequest
用户代理:Mozilla / 5.0(Macintosh;英特尔Mac OS X 10_7_5)AppleWebKit / 537.22(KHTML,像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

这些是相关的响应 ,显示正在正确设置 Content-Length Content-Type

  HTTP / 1.1 200 OK 
高速缓存控制:无存储,无高速缓存,必须重新生效, 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
服务器: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

为什么值得,我已经在标准(http)和安全(https)连接上尝试过,没有差异:内容在浏览器中正常加载,但未被Progress API处理。






a href =http://stackoverflow.com/a/15172401/350278> Adam的建议,我试图将服务器端切换到gzip编码,没有成功或更改。以下是相关的响应头:

  HTTP / 1.1 200 OK 
缓存控制: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
到期时间:Thu,19 Nov 1981 08:52:00 GMT
P3P:CP =CAO PSA OUR
Pragma:no-cache
服务器: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-长度:28250
连接:keep-alive

只是重复:正在下载内容并正确解码,它只是我遇到麻烦的进度API。






Bertrand的请求,此处请求:

  $。ajax({
url:'< url snipped>',
data:{},
success:onDone,
dataType:'json',
cache:true,
progress:onProgress || function(){}
});

这里是 onProgress m使用(不要太疯狂):

 函数(jqXHR,evt)
{
//是的,我知道这产生无穷有时
var pct = 100 * evt.position / evt.total;

//只是一个更新一些样式和javascript的方法
updateProgress(pct);
});


解决方案

我无法解决使用 onProgress 压缩内容本身,但我想出了这个半简单的解决方法。 简单:在 GET HEAD 请求>请求,并在有足够的信息时呈现进度条。






 函数加载器(onDone,onProgress,url,data)
{
// onDone =在成功下载时运行的事件处理程序
// onProgress =在下载期间运行的事件处理程序
// url = url to load
// data =使用AJAX请求发送的额外参数
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);
}
});
}

然后使用它:

  loader(function(response)
{
console.log(Content loaded!do stuff now。);
}
function(pct)
{
console.log(内容是+ pct +%loaded。);
},
'< url这里>',{});






在服务器端,将<$ c GET HEAD 请求上的$ c> X-Content-Length (应代表未压缩的内容长度),并中止在 HEAD 请求上发送内容。



在PHP中,设置标题如下:

  X-Content-Length:.strlen($ payload)); 

然后如果是 HEAD request:

  if($ _SERVER ['REQUEST_METHOD'] ==HEAD)
{
exit;
}






在操作中:





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函数使用deflated / gzipped内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆