首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >XMLHttpRequest进度跟踪和Symfony (CORS)

XMLHttpRequest进度跟踪和Symfony (CORS)
EN

Stack Overflow用户
提问于 2017-01-28 12:38:28
回答 1查看 1.2K关注 0票数 2

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

一切运行良好,但我希望在下一页异步加载时添加一个加载栏。不幸的是,lengthComputable参数是假的。为了了解这个问题,我在发送带有以下行的响应之前在Symfony3上设置了头:

代码语言:javascript
复制
$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又是错误的。

下面是使用这四行代码的响应头:

代码语言:javascript
复制
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

(是的,有些参数是两次出现的)

我认为这与跨源头策略有关,但我找不到解决方案。

我尝试过设置其他参数,比如

代码语言:javascript
复制
$responseHeaders->set('Access-Control-Allow-Headers', 'content-type');

甚至

代码语言:javascript
复制
$responseHeaders->set('Access-Control-Allow-Origin', '*');

我跟踪并尝试了这些链接

Symfony2. I can't set Content-Length header

How can I access the Content-Length header from a cross domain Ajax request?

CORS with php headers

编辑

我不使用任何代理,我的服务器托管在OVH上。

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

代码语言:javascript
复制
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();

在我的progress(evt)函数中,通过记录evt参数,可以看出lengthComputable是假的。

在我的complete(evt)函数中,我将evt.target.response放在相应的div中。

我只想补充一点,当我使用这段代码时,我会在大约500 is之后收到最后一个进度事件,加载长度等于页面的总长度(所以文档的长度是100%,但是lengthComputable是假的,total等于0,所以我知道这仅仅是因为在加载结束时我会查看标题),但是调用complete函数需要大约5秒。我觉得这是因为不知道内容的长度。无论如何,当我删除我的4行代码(第一行)时,这个问题消失了,但我始终没有lengthComputable=true .

谢谢你抽出时间!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-01-30 21:30:51

因此,问题在于Apache的mod_deflate压缩了GZIP中的响应,导致长度不能被计算的客户端(因为Apache将响应块从我所读到)。

快速和肮脏的解决方案是在Apache设置中禁用该URL的压缩。如果您不能,或者不想为了使其优化,可以从here获得更详细的解决方案。

代码语言:javascript
复制
$event->getResponse()->headers->set('x-decompressed-content-length', $length);

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

代码语言:javascript
复制
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是基于压缩的还是解压的响应,但是在该页面上还有其他可能的线索。

您也可以尝试这一点,从here中获取,我将允许您将其翻译到Symfony,因为我不确定您是如何设置内容的,但基本上是关于预先对数据进行压缩,这样Apache就不会这么做了。

代码语言:javascript
复制
    // 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

因此,可以在控制器中添加前两个if,或者设置响应的内容,并保持事件侦听器的原样。

这是我能找到的两种最不“讨厌”的解决方案。不仅如此,你的猜测和我的一样好,我以前没试过。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41909881

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档