在Apache中启用gzip压缩时未发送Content-Length?


13

我非常感谢您对理解Apache行为的一些帮助。

我正在从application / json中的iPhone Objective-C应用程序与PHP通信。Gzip压缩在服务器上启用,并由客户端请求。

从我的.htaccess:

AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-httpd-php application/json

对于较小的请求,Apache会设置“ Content-Length”标头。例如(这些值从标头在Objective-C中输出):

Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Length" = 185;     <-------------
"Content-Type" = "application/json";
Date = "Wed, 22 Sep 2010 12:20:27 GMT";
"Keep-Alive" = "timeout=3, max=149";
Server = Apache;
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/5.2.13";
"X-Uncompressed-Content-Length" = 217;

X-Uncompressed-Content-Length是一个标头,我将其设置为未压缩的JSON字符串的大小。

如您所见,此请求非常小(217字节)。

这是来自较大请求(282888字节)的标头:

Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Type" = "application/json";
Date = "Wed, 22 Sep 2010 12:20:29 GMT";
"Keep-Alive" = "timeout=3, max=148";
Server = Apache;
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/5.2.13";
"X-Uncompressed-Content-Length" = 282888;

注意,没有给出Content-Length。

我的问题:

  1. Apache为什么不为较大的请求发送Content-Length?
  2. 设置'Contend-Encoding = gzip'的事实是否意味着即使我无法验证大小差异,gzip压缩仍可处理较大的请求?
  3. 有没有办法让Apache为这些较大的请求包括实际的Content-Length,以更准确地向用户报告数据使用情况?

该应用程序可以用于昂贵的数据计划,因此我希望向用户报告实际使用情况,而不是夸大使用率的30-70%(几百个额外的KB听起来可能并不多-但这些计划的成本在1美元之间以及每MB 10美元!)。

提前致谢。

Answers:


14

除了Martin Fjordvalds答案:

仅当压缩文件的大小大于DeflateBufferSize时,Apache才使用分块编码。因此,增加此缓冲区的大小将阻止服务器也对较大的文件使用分块编码,从而导致即使对于压缩数据也要发送Content-Length。

可在此处获得更多信息:http : //httpd.apache.org/docs/2.2/mod/mod_deflate.html#deflatebuffersize


好一个。这可能是解决此问题的最快方法。如果有人需要更高级别的自定义(例如,对某些请求进行分块处理,而不对其他请求进行分块处理),请参阅我的答案serverfault.com/a/183856/54957,以获取手动解决方案。
威廉·丹尼斯

7

听起来Apache正在执行分块编码,这意味着它可以在压缩数据时发送数据,而不必等待压缩完整的响应。这是相当标准的做法,不过我对Apache还不十分了解,无法说是否可以禁用它。


感谢您提供的信息,您为我指明了正确的方向,我已解决了该问题。
威廉·丹尼斯

公认。对于任何阅读此问题的人,请阅读我的答案以获取详细的解决方案。基本上,您可以通过手动缓冲和压缩回复来避免分块(因此,内容长度为零)。
威廉·丹尼斯

令人困惑的是,可接受的答案不是原始问题的答案,而是可以帮助您找到答案的东西。也许您应该接受您在下面发布的答案,以使事情更加清楚。
redbmk

@redbmk公平的观点,我只是不想显得忘恩负义。Philippe实际上对此具有完美的简单解决方案,因此我已经接受了他的遗嘱。
威廉·丹尼斯

5

好的,我设法解决了这个问题。正如Martin F正确指出的那样,Apache正在对答复进行分块处理,因此内容大小未知。对于许多人来说,这是理想的(页面加载速度更快)。这是以无法报告下载进度为代价的。

对于那些像我一样真正想报告下载进度的人,如果您使用Apache或PHP的自动gzip支持,则无能为力。解决方案是手动执行。这比听起来容易:

如果要发送整个文件,则这是PHP中强制使用单个块(具有Content-Length)的一个很好的示例:http : //www.php.net/manual/zh/function.ob-start.php #94741

如果您要发送生成的数据,则使用gzencode对数据进行编码,就像上面的示例一样。前提条件是所有输出数据都存储在变量中(如果需要缓冲,然后获取缓冲的内容,可以使用ob_start帮助此操作)。

        // $replyBody is the entire contents of your reply

        header("Content-Type: application/json");  // or whatever yours is

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

        // if supported, gzips data
        if($pack) {
            header("Content-Encoding: gzip");
            $replyBody = gzencode($replyBody, 9, FORCE_GZIP);
        }

        // compressed or not, sets the Content-Length           
        header("Content-Length: " . mb_strlen($replyBody, 'latin1'));

        // outputs reply & exits
        echo $replyBody;
        exit;

瞧!

自己执行此操作的另一个好处是可以设置压缩级别。这对于我的移动应用程序非常有用,因为我可以将其设置为最高压缩级别(因此,我的用户为数据支付的费用更少!)–而服务器可能仅使用中等压缩级别以获得更好的CPU /大小权衡。我相信压缩级别是必须更改的,除非您可以编辑httpd.conf(在共享主机上,我不能)。

因此,我保留了DEFLATE .htaccess指令,除了我现在以上述方式编码的application / json答复以外的所有内容。

再次感谢Martin F,您给了我解决该问题所需的火花:)


1
顺便说一下,JSON数据的积蓄(有重重复键)是巨大的,在一种情况下减少了77%。每MB 1美元的价格真是个
数目

1
您可能应该只使用strlen($replyBody)而不是mb_strlen($replyBody, 'latin1')。content-length只是字节数(不是字符),这是strlen()给您的。由于latin1字符始终为8位,因此将mb_strlen()与'latin1'结合使用时会产生问题,但编码产生的字节无效的latin1字符可能会出现问题。
2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.