PHP 手册 GZip 编码

20

我正在使用Page Speed测试我的网站,结果得分约为70/100。启用压缩是减缓网站速度的第一个最重要因素。

我知道可以通过修改php.ini来自动执行压缩,但我更感兴趣的是手动方法(gzencode)。

问题在于所有浏览器要么无法打开网站(Firefox:“由于使用了无效或不受支持的压缩形式,无法显示您要查看的页面。”,Chrome:“303,ERR内容编码”等),要么它们只会显示编码后的字符串。

Live Headers 显示浏览器接受了编码,并且响应设置了内容类型,但仍然失败。

GET / HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate

HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Length: 5827
Vary: Accept-Encoding

private function _compress($data) {
    //return trim(preg_replace(array('/\>[^\S ]+/s','/[^\S ]+\</s','/(\s)+/s'), array('>','<','\\1'), $data));
    $supportsGzip = strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false;

    ob_start();
    if ($supportsGzip) {
        echo gzencode(trim(preg_replace('/\s+/', ' ', $data)), 9);
    } else {
        echo $data;
    }

    $content = ob_get_contents();
    header("content-type: text/html; charset: UTF-8");
    header("cache-control: must-revalidate");
    $offset = 60 * 60;
    $expire = "expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT";
    header($expire);
    header('Content-Length: ' . strlen($content));
    header('Vary: Accept-Encoding');
    ob_end_clean();
    echo $content;
}
如果我将Content-Encoding更改为zlib,我会得到编码后的字符串:
‹������ÕZÿsÛ¶ÿW^‘¥²o‘¨/–-Ë–Ú؉_Ôµ•õÚ_v I°I‚!A©j–Öºnçÿb·»%ÍÚë²nëå?‘þ›=€¤L)’,ÛIw>ŸEâxïáƒ÷°ùÞ½O¶Ÿï߇Žtlؼµ·» $kŸ•¶ ã^ã<܃•\¾� Ÿº—\¸Ô6ŒûŽ”^Õ0z½^®WÊ ¿m4ÅjÅ°…XÎ’©Ã¦ænS·]#ÌÕF-|8LRPL²ìIÈ»5²-\É\™mô=FÀŒJ5"Ù—RóÝ�³Cý€ÉZ([ÙŠb%¹´YýÑãáîcx}±iD´˜¿KV#4”á§x>¬°à®íÒ ãpÅËæî1øÌ®‘@öm

我现在不太关心如何压缩,只是想知道为什么它不能正常工作。

干杯!


如果 $supportsGzip,那么 Content-Length 应该是压缩后内容的长度,对吗? - Fanis Hatzidakis
@Fanis,contents 包含缓冲区的内容,根据 $supportsGzip 的值,可以是编码后的或者是未经处理的纯文本。 - M.Alnashmi
奇怪,如果你使用ob_gzhandler()而不是自己编写压缩包装器,你会得到相同的行为吗? - symcbean
@user687976,你说得完全正确,我在寻找$data时错过了那一行。实际上,正如@Craig Sefton在他的答案中提到的那样,这可能是问题所在?$data与$content之间的区别。 - Fanis Hatzidakis
3个回答

16

我认为这是因为你试图压缩一个空字符串。

我按照你给出的脚本在FF和IE中运行,两者都失败了,FF报告了问题(就像你描述的那样)。

然而,我注意到$data是一个空字符串。

当我在文件顶部设置$data = "一些测试数据。"时,它立即工作了(浏览器显示“一些测试数据。”),并且在Firebug中检查,我可以看到正确的头信息。

Content-Encoding    gzip
Content-Length  68
Vary    Accept-Encoding
Content-Type    text/html

编辑: 另外,需要指出的是,你的 if ($supportsGzip) { 有点奇怪,因为你的 else 条件应该实际上输出 $data,而不是 $content

编辑: 好的,根据你上面修改后的函数,有两个关键问题。

首要问题与你调用 ob_end_clean() 导致头部信息被清除有关。在PHP文档 的一条评论中指出 "ob_end_clean() 会丢弃头信息"。

这意味着在调用 ob_end_clean() 之前设置的任何头部信息都将被清除。另外,你修改后的函数也没有发送 gzip 编码的头信息。

其实,在这里甚至可能不需要使用 ob_start 和相关函数。尝试以下代码:

function _compress( $data ) {

    $supportsGzip = strpos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false;


    if ( $supportsGzip ) {
        $content = gzencode( trim( preg_replace( '/\s+/', ' ', $data ) ), 9);
        header('Content-Encoding: gzip');
    } else {
        $content = $data;
    }

    $offset = 60 * 60;
    $expire = "expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT";

    header("content-type: text/html; charset: UTF-8");
    header("cache-control: must-revalidate");
    header( $expire );
    header( 'Content-Length: ' . strlen( $content ) );
    header('Vary: Accept-Encoding');

    echo $content;

}

_compress( "Some test data" );

这在IE和FF中有效,但我没有时间测试其他浏览器。

如果您确实必须使用ob_start及其相关函数,请确保在调用ob_end_clean()后设置标题。


GZIP不是加密算法,而是一种压缩算法。 - Gumbo
数据字符串是函数参数,代码是代码片段。 我甚至尝试了 data = "一些字符串",但仍然未能通过测试。 - M.Alnashmi
@user687976 - 刚刚更新了我的回答,并附上了更多信息。希望对你有所帮助。 - Craig Sefton
如果不支持$ supportGzip,则无需调用“header('Content-Encoding:gzip');”。 - lazycommit

8
我建议使用http://php.net/manual/de/function.ob-gzhandler.php,这对我来说是开箱即用的:
在我的index.php文件中,我只需在某些输出之前放置以下内容:
    /**
     * Enable GZIP-Compression for Browser that support it.
     */
    ob_start("ob_gzhandler");

并将其编码!

1
如果 ($supportsGzip) { ob_start("ob_gzhandler"); echo trim(preg_replace('/\s+/', ' ', $data)); } 尝试了这个但仍然无法打开。在调用此函数之前没有任何输出被显示。 - M.Alnashmi
你不需要在前面加上if(),因为PHP会为你检查(来自文档):ob_gzhandler()旨在用作ob_start()的回调函数,以帮助发送gz编码数据到支持压缩网页的Web浏览器。在ob_gzhandler()实际发送压缩数据之前,它确定浏览器将接受哪种类型的内容编码(“gzip”,“deflate”或根本没有),并相应地返回其输出。...如果浏览器不支持压缩页面,则此函数返回FALSE。 - daschl
有一种方法可以检查用户是否支持编码,以下代码应该放在文档的最开始处: if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) ob_start("ob_gzhandler"); else ob_start();在这种情况下不需要使用ob_flush(它会自动添加)。 - Zydnar

0
一些事情:
  1. 你可能想要添加另一个头文件: header('Content-Encoding: gzip');

  2. 你正在使用ob_end_clean,它会删除所有已发出/打印的内容而不将其发送到浏览器。根据你想要做什么,你可能需要使用ob_flush。

  3. 为了确保输出被缓冲和处理(如果你使用PHP的输出缓冲压缩,则进行压缩),请确保所有echo/print语句都位于ob_start和ob_flush语句之间。

--然后再试一次吧 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接