【问题标题】:XMLHttpRequest progress tracking and Symfony (CORS)XMLHttpRequest 进度跟踪和 Symfony (CORS)
【发布时间】:2017-06-14 01:46:41
【问题描述】:

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

一切正常,但我想在下一页异步加载时添加一个加载栏。不幸的是,lengthComputable 参数是错误的。 为了解决这个问题,我在 Symfony3 上设置了标题,然后用这些行发送响应:

$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 又是假的。

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

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

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

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

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

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

甚至

$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 代码(没什么特别的)

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 毫秒后收到最后一个进度事件,加载的长度等于页面的总长度(所以是文档的 100%,但 lengthComputable 是假的total 等于 0,所以我知道它只是因为我在加载结束时查看我的标题) 但是调用complete 函数需要大约5 秒。我认为这是因为内容长度未知。无论如何,当我删除我的 4 行代码(第一行)时,这个问题就消失了,但我总是没有 lengthComputable=true ...

感谢您的宝贵时间!

【问题讨论】:

  • 发布您的 javascript。您是否使用相同的浏览器进行测试?您的开发服务器是否在代理后面?
  • 我编辑了我的帖子,所以我尝试了 chrome、firefox、opera 和 safari,结果相同。不,我没有使用代理,我的服务器由 OVH 托管(专业版)
  • 您是否使用与本地相同的内容编码? GZIP 可能是这里的问题
  • 其实我不太明白:我的页面是html页面,所以在本地和远程服务器一样,我使用纯文本编码。
  • Content-Encoding:gzip 表示 Apache 正在使用 gzip 压缩响应,因此您在 PHP 中设置的长度可能不正确。检查 localhost 中的标头以查看 Content-Encoding 是否为相同的值。

标签: php http-headers xmlhttprequest cors symfony


【解决方案1】:

所以问题是 Apache 的 mod_deflate 正在压缩 GZIP 中的响应,导致客户端无法计算长度(因为 Apache 将我读到的响应分块)。

快速而肮脏的解决方案是在您的 Apache 设置中禁用该 URL 的压缩。如果您不能或不想保持优化,可以从here 获取更详细的解决方案

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

那么你的进度函数应该类似于:

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,因为我不确定你是如何设置你的内容的,但基本上它是关于事先自己 gzip 数据以便 Apache 做不要这样做。

    // 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,或者在您设置响应内容并保持事件侦听器不变的任何位置。

这是我能找到的两个最不“老套”的解决方案。另外,你的猜测和我的一样好,我之前没试过。

【讨论】:

  • 设置x-decompressed-content-length 有效,我很惊讶,但evt.progress 似乎是加载内容的未压缩大小,所以效果很好。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2015-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多