问题 PHP手动GZip编码


我用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‚!Aj–Öº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

我不太关心压缩,因为我想知道为什么它不起作用。

干杯,


7401
2018-04-18 05:51


起源

+1有趣的问题 - Wh1T3h4Ck5
如果它 $supportsGzip 不能 Content-Length 取而代之的是gzip压缩内容的长度? - Fanis
@Fanis,内容保存缓冲区的内容,无论是编码还是普通,取决于$ supportsGzip。 - M.Alnashmi
奇怪的是,如果使用ob_gzhandler()而不是滚动自己的压缩包装,你会得到相同的行为吗? - symcbean
@ user687976你是绝对正确的,我在寻找$ data时设法错过了那条线。实际上,正如@Craig Sefton在他的回答中提到的,这可能是问题吗? $ data vs $ content - Fanis


答案:


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

我把你的脚本放在你的脚本上,并在FF和IE中运行它。

两者都失败了,FF说有一个问题(就像你描述的那样)。

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

我订的时候 $data = "Some test data."; 在它立即工作的文件的顶部(浏览器显示“一些测试数据。”),并检查Firebug,我可以看到正确的标题。

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

编辑: 另外,只是要指出,你的 if ($supportsGzip) { 有点奇怪,因为你的其他条件实际应该回应 $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()


10
2018-04-18 10:36



GZIP不是加密算法,而是压缩算法。 - Gumbo
@Gumbo Doh,对不起,大脑思考不对。固定。 - Craig Sefton
数据字符串是一个函数参数,代码是一个片段。我甚至试过了 data= "Some string" 并且仍未通过测试。 - M.Alnashmi
@ user687976 - 刚刚用更多信息更新了我的答案。希望有所帮助。 - Craig Sefton
无需调用 header('Content-Encoding: gzip'); 如果不 $supportGzip。 - lazycommit


我建议使用 http://php.net/manual/de/function.ob-gzhandler.php,这对我来说开箱即用:

在我的index.php中,我只是将它放在一些输出之前:

    /**
     * Enable GZIP-Compression for Browser that support it.
     */
    ob_start("ob_gzhandler");

它编码了!


6
2018-04-18 13:00



if ($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


一些东西:

  1. 您可能想要添加另一个标头: header('Content-Encoding:gzip');

  2. 您正在使用ob_end_clean,它删除所有回显/打印的内容而不将其发送到浏览器。根据您要执行的操作,您可能希望使用ob_flush。

  3. 要确保缓冲和处理输出(并在使用PHP的输出缓冲压缩时进行压缩),请确保所有echo / print语句都放在ob_start和ob_flush sttements之间。

- 然后再试一次:)


0
2018-04-20 21:19