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

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

问题描述

我之前看到过很多与此类似的问题,但我还没有找到一个准确描述我当前问题的问题,所以这里是:

我有一个通过 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-LengthContent-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 + "%已加载.");},'<这里的网址>', {});

<小时>

在服务器端,在 GETHEAD 请求上设置 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 函数使用压缩/压缩的内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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