XMLHttpRequest进度跟踪和Symfony(CORS) [英] XMLHttpRequest progress tracking and Symfony (CORS)

查看:106
本文介绍了XMLHttpRequest进度跟踪和Symfony(CORS)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在ovh服务器上使用Symfony框架创建一个网站。
当用户更改页面时,我会创建一个新的 XMLHttpRequest ,以避免重新加载所有页面并改善用户体验。

I'm making a website using the Symfony framework, on an ovh server. When an user changes page, I create a new XMLHttpRequest to avoid to reload all the page and improve user experience.

一切正常,但我想在下一页异步加载时添加一个加载栏。不幸的是, lengthComputable 参数为false。
要解决这个问题,我在发送带有这些行的响应之前就已经在Symfony3上设置了标题:

Everything works well, but I want to add a loading bar while the next page is loading asynchronously. Unfortunately the lengthComputable parameter was false. To go through this issue, I've set the header on Symfony3 just before sending the response with theses lines :

$length = strlen($event->getResponse()->getContent());
$event->getResponse()->headers->set('Content-Length', $length);
$event->getResponse()->headers->set('Accept-Ranges', "bytes");
$event->getResponse()->sendHeaders();

此技巧适用于我的本地开发服务器, lengthComputable 设置为true,我可以计算当前的负载百分比。但是当我把所有东西放在我的远程开发服务器上时, lengthComputable 再次为假。

This trick works in my local development server, lengthComputable is set to true and I can calculate the current load percentage. But when I put everything on my remote dev server, lengthComputable is false again.

这是使用的响应头这四行:

Here is the response header using theses four lines :

Accept-Ranges:bytes
Accept-Ranges:bytes
Cache-Control:no-cache
Cache-Control:no-cache
Content-Encoding:gzip
Content-Length:3100
Content-Length:3100
Content-Type:text/html; charset=UTF-8
Date:Sat, 28 Jan 2017 12:34:08 GMT
Server:Apache
Set-Cookie:PHPSESSID=***; path=/; HttpOnly
Vary:Accept-Encoding

(是的,有些参数存在两次)

(And yes, some parameters are present two times)

我认为这与跨域标题政策有关,但我找不到解决方案。

I thing this is related to cross origin header policy, but I can't found a solution.

我试图设置其他参数,例如

I've tried to set others parameters like

$responseHeaders->set('Access-Control-Allow-Headers', 'content-type');

甚至

$responseHeaders->set('Access-Control-Allow-Origin', '*');

我关注并尝试了这些链接

I followed and tried theses links

Symfony2。我无法设置Content-Length标题

如何从跨域Ajax请求中访问Content-Length头?

带有php标题的CORS

编辑

我没有使用任何代理和我的服务器在OVH上托管。

I'm not using any proxy, and my server is hosted on OVH.

这是我的javascript代码(没有例外)

Here is my javascript code (nothing exceptional)

ajax_oReq = new XMLHttpRequest();
ajax_oReq.addEventListener("progress", progress, false);
ajax_oReq.addEventListener("load", complete, false);
ajax_oReq.addEventListener("error", error, false);
ajax_oReq.addEventListener("abort", error, false);

params = "ajax=1";
if(url.indexOf('?')!=-1){ params = "&"+params; }else{ params = "?"+params; }

ajax_oReq.open("GET", url+params, true);
ajax_oReq.send();

在我的进度(evt)函数中,通过记录 evt 参数,我可以看到 lengthComputable 为false。

In my progress(evt) function, I can see that lengthComputable is false by logging the evt parameter.

在我的完成(evt)函数中,我将 evt.target.response 放入相应的 div

In my complete(evt) function, I put evt.target.response in my corresponding div.

我只想补充一点,当我使用这段代码时,我会在大约500毫秒后收到最后一个进度事件加载长度等于页面的总长度(因此文档的100%,但 lengthComputable 为false且总计等于0所以我知道它只是因为我在加载结束时查看我的标题)
完成函数需要大约5秒钟叫做。我这是因为内容长度未知。无论如何,当我删除我的4行代码(第一行)时,这个问题就消失了,但我总是没有 lengthComputable = true ...

I just want to add that when I use this code, I receive the last progress event after about 500ms with a loaded length equal to the total length of the page (so 100% of the document, but lengthComputable is false and total is equal to 0 so I know it just because I look my headers when the loading is end) But it takes about 5 more seconds for the complete function to be called. I thing this is because the content length isn't known. Anyway, when I remove my 4 lines of code (the first ones), this issue disappear but I always don't have lengthComputable=true ...

感谢您的时间!

推荐答案

所以问题是Apache的mod_deflate压缩了在GZIP中的响应,导致长度不是可计算的客户端(因为Apache根据我读过的内容来调整响应)。

So the problem was that Apache's mod_deflate was compressing the response in GZIP, resulting in the length not being computable client side (because Apache chunks the response from what I've read).

快速&脏解决方案是在Apache设置中禁用该URL的压缩。如果你不能,或者不想为了保持优化,那么这里有一个更精细的解决方案

The quick & dirty solution is to disable compression for that URL in your Apache settings. If you cannot, or do not want to in order to keep it optimized, there is a more elaborate solution taken from here

$event->getResponse()->headers->set('x-decompressed-content-length', $length);

然后您的进度函数应如下所示:

then your progress function should look something like:

var progress = function (e) {
      var contentLength;
      if (e.lengthComputable) {
        contentLength = e.total;
      } else {
        contentLength = e.target.getResponseHeader('x-decompressed-content-length');
      }
      var progress = (e.loaded / contentLength) * 100;
    };

我不确定它会运作得那么好,这取决于 e.loaded 基于压缩或解压缩的响应,但在该页面上还有其他可能的潜在客户

I'm not sure it will work that well tho, it depends if e.loaded is based on the compressed or decompressed response, but there are other possible leads for you on that page

你也可以尝试这个,从这里,我会让你翻译成Symfony,因为我不确定你是如何设置你的内容但基本上它是关于事先自己压缩数据,以便Apache不这样做。

You could also try this, taken from here, i will let you translate it to Symfony because I'm not sure of how you are setting your content but basically it is about gzipping the data yourself beforehand so that Apache does not do it.

    // checks if gzip is supported by client
    $pack = true;
    if(empty($_SERVER["HTTP_ACCEPT_ENCODING"]) || strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') === false) //replace $_SERVER part with SF method
    {
        $pack = false;
    }

    // if supported, gzips data
    if($pack) {
        $replyBody = gzencode($replyBody, 9, FORCE_GZIP); // Watch out 9 is strong compression
        // Set SF's Response content with gzipped content here
        header("Content-Encoding: gzip"); // replace that with appropriate SF method
    } else {
        // Set SF's Response content with uncompressed content here
    }

    // compressed or not, sets the Content-Length           
    header("Content-Length: " . strlen($replyBody)); // replace that with appropriate SF methods

所以几乎要添加前两个如果在您的控制器中,或者您设置响应内容的任何地方,并保持您的事件监听器不变。

So pretty much add the first two if in your controller, or wherever you set the Response's content and keep your Event Listener as is.

这两个是最少的hacky 我能找到的解决方案。除此之外,你的猜测和我的一样好,我以前没试过。

Those are the two the least "hacky" solutions I could find. Further than that, your guess is as good as mine, I haven't tried it before.

这篇关于XMLHttpRequest进度跟踪和Symfony(CORS)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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