我在ovh服务器上使用Symfony框架创建一个网站。
当用户更改页面时,我会创建一个新的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)
函数中,我可以通过记录lengthComputable
参数看到evt
为false。
在我的complete(evt)
函数中,我将evt.target.response
放入相应的div
。
我只想补充一点,当我使用这段代码时,我收到大约500毫秒后的最后一个进度事件,其加载长度等于页面的总长度(因此100%的文档,但是lengthComputable
是false并且total
等于0所以我知道它只是因为我在加载结束时查看我的标题
但是调用complete
函数需要大约5秒钟。我这是因为内容长度未知。无论如何,当我删除我的4行代码(第一行)时,这个问题就消失了,但我总是没有lengthComputable=true
......
谢谢你的时间!
答案 0 :(得分:2)
所以问题是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
,或者在设置响应内容的任何地方添加,并保持事件监听器不变。
这两个是最少的" hacky"我能找到的解决方案。除此之外,你的猜测和我的一样好,我以前没有尝试过。