我正在使用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?
编辑
我不使用任何代理,我的服务器托管在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 is之后收到最后一个进度事件,加载长度等于页面的总长度(所以文档的长度是100%,但是lengthComputable是假的,total等于0,所以我知道这仅仅是因为在加载结束时我会查看标题),但是调用complete函数需要大约5秒。我觉得这是因为不知道内容的长度。无论如何,当我删除我的4行代码(第一行)时,这个问题消失了,但我始终没有lengthComputable=true .
谢谢你抽出时间!
发布于 2017-01-30 21:30:51
因此,问题在于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,因为我不确定您是如何设置内容的,但基本上是关于预先对数据进行压缩,这样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,或者设置响应的内容,并保持事件侦听器的原样。
这是我能找到的两种最不“讨厌”的解决方案。不仅如此,你的猜测和我的一样好,我以前没试过。
https://stackoverflow.com/questions/41909881
复制相似问题