gzip 压缩时数据损坏

发布于 2024-08-29 12:42:24 字数 2921 浏览 2 评论 0原文

这是我为网站上的 gzip 内容编写的脚本,位于“gzip.php”中。我使用它的方式是,在我想要启用 gzipping 的页面上,我将文件包含在顶部,并在底部调用输出函数,如下所示:

print_gzipped_page('javascript')

如果文件是 css 文件,我使用 'css' 作为 $类型参数,如果它是一个 php 文件,我调用该函数而不声明任何参数。该脚本在所有浏览器中都可以正常工作,但 Opera 除外,它会给出错误,指出由于数据损坏而无法解码页面。谁能告诉我我做错了什么?

<?php
function print_gzipped_page($type = false) {
    if(headers_sent()){
        $encoding = false;
    }
    elseif( strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false ){
        $encoding = 'x-gzip';
    }
    elseif( strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false ){
        $encoding = 'gzip';
    }
    else{
        $encoding = false;
    }
    if ($type!=false) {
        $type_header_array = array("css" => "Content-Type: text/css", "javascript" => "Content-Type: application/x-javascript");
        $type_header = $type_header_array[$type];
    }

    $contents = ob_get_contents();
    ob_end_clean();
    $etag = '"' .  md5($contents) . '"';
    $etag_header = 'Etag: ' . $etag;
    header($etag_header);

    if ($type!=false) {
        header($type_header);
    }

    if (isset($_SERVER['HTTP_IF_NONE_MATCH']) and $_SERVER['HTTP_IF_NONE_MATCH']==$etag) {
        header("HTTP/1.1 304 Not Modified");
        exit();
    }

    if($encoding){
        header('Content-Encoding: '.$encoding);
        print("\x1f\x8b\x08\x00\x00\x00\x00\x00");
        $size = strlen($contents);
        $contents = gzcompress($contents, 9);
        $contents = substr($contents, 0, $size);
    }

    echo $contents;
    exit();
}

ob_start();
ob_implicit_flush(0);
?>

附加信息:如果被压缩的文档长度仅为 10-15 个字符,则该脚本可以正常工作。

感谢您的帮助,更正版本:

<?php
function print_gzipped_page($type = false) {
    if(headers_sent()){
        $encoding = false;
    }
    elseif( strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false ){
        $encoding = 'x-gzip';
    }
    elseif( strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false ){
        $encoding = 'gzip';
    }
    else{
        $encoding = false;
    }
    if ($type!=false) {
        $type_header_array = array("css" => "Content-Type: text/css", "javascript" => "Content-Type: application/x-javascript");
        $type_header = $type_header_array[$type];
        header($type_header);
    }

    $contents = ob_get_contents();
    ob_end_clean();

    $etag = '"' .  md5($contents) . '"';
    $etag_header = 'Etag: ' . $etag;
    header($etag_header);

    if (isset($_SERVER['HTTP_IF_NONE_MATCH']) and $_SERVER['HTTP_IF_NONE_MATCH']==$etag) {
        header("HTTP/1.1 304 Not Modified");
        exit();
    }

    if($encoding){
        header('Content-Encoding: ' . $encoding);
        $contents = gzencode($contents, 9);
    }

    $length = strlen($contents);
    header('Content-Length: ' . $length);
    echo $contents;
    exit();
}

ob_start();
ob_implicit_flush(0);
?>

This is the script I have written for gzipping content on my site, which is located in 'gzip.php'. The way I use it is that on pages where I want to enable gzipping I include the file at the top and at the bottom I call the output function like this:

print_gzipped_page('javascript')

If the file is a css-file I use 'css' as the $type-argument and if its a php file I call the function without declaring any arguments. The script works fine in all browsers except Opera which gives an error saying it could not decode the page due to damaged data. Can anyone tell me what I have done wrong?

<?php
function print_gzipped_page($type = false) {
    if(headers_sent()){
        $encoding = false;
    }
    elseif( strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false ){
        $encoding = 'x-gzip';
    }
    elseif( strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false ){
        $encoding = 'gzip';
    }
    else{
        $encoding = false;
    }
    if ($type!=false) {
        $type_header_array = array("css" => "Content-Type: text/css", "javascript" => "Content-Type: application/x-javascript");
        $type_header = $type_header_array[$type];
    }

    $contents = ob_get_contents();
    ob_end_clean();
    $etag = '"' .  md5($contents) . '"';
    $etag_header = 'Etag: ' . $etag;
    header($etag_header);

    if ($type!=false) {
        header($type_header);
    }

    if (isset($_SERVER['HTTP_IF_NONE_MATCH']) and $_SERVER['HTTP_IF_NONE_MATCH']==$etag) {
        header("HTTP/1.1 304 Not Modified");
        exit();
    }

    if($encoding){
        header('Content-Encoding: '.$encoding);
        print("\x1f\x8b\x08\x00\x00\x00\x00\x00");
        $size = strlen($contents);
        $contents = gzcompress($contents, 9);
        $contents = substr($contents, 0, $size);
    }

    echo $contents;
    exit();
}

ob_start();
ob_implicit_flush(0);
?>

Additional info: The script works if the length of the document being compressed is only 10-15 characters.

Thanks for the help, corrected version:

<?php
function print_gzipped_page($type = false) {
    if(headers_sent()){
        $encoding = false;
    }
    elseif( strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false ){
        $encoding = 'x-gzip';
    }
    elseif( strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false ){
        $encoding = 'gzip';
    }
    else{
        $encoding = false;
    }
    if ($type!=false) {
        $type_header_array = array("css" => "Content-Type: text/css", "javascript" => "Content-Type: application/x-javascript");
        $type_header = $type_header_array[$type];
        header($type_header);
    }

    $contents = ob_get_contents();
    ob_end_clean();

    $etag = '"' .  md5($contents) . '"';
    $etag_header = 'Etag: ' . $etag;
    header($etag_header);

    if (isset($_SERVER['HTTP_IF_NONE_MATCH']) and $_SERVER['HTTP_IF_NONE_MATCH']==$etag) {
        header("HTTP/1.1 304 Not Modified");
        exit();
    }

    if($encoding){
        header('Content-Encoding: ' . $encoding);
        $contents = gzencode($contents, 9);
    }

    $length = strlen($contents);
    header('Content-Length: ' . $length);
    echo $contents;
    exit();
}

ob_start();
ob_implicit_flush(0);
?>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

但可醉心 2024-09-05 12:42:24

这个方法有点太笨拙了。而是使用 ob_gzhandler。它将自动对客户端支持的内容进行 GZIP 压缩并设置必要的标头。

ob_start('ob_gzhandler');
readfile($path);

This approach is a bit too clumsy. Rather make use of ob_gzhandler. It will automatically GZIP the content which the client supports it and set the necessary headers.

ob_start('ob_gzhandler');
readfile($path);
薄情伤 2024-09-05 12:42:24

有两件事值得注意:

1)您似乎没有将 Content-Length 标头设置为压缩数据的大小。 (也许我忽略了它。)如果您不设置此项,浏览器可能会认为您太早完成了数据发送。

2)您正在使用未压缩的$size对压缩的$content进行substr。当内部结构有 EOF 标记时,某些浏览器将停止解压缩,但其他浏览器(Opera?)可能会尝试解压缩整个下载的缓冲区。这肯定会给你一个“数据损坏”错误。对于小缓冲区,您可能不会看到此问题,因为开销量和压缩量可能完全匹配。

Two things stand out:

1) you don't seem to be setting the Content-Length header to the size of the compressed data. (Maybe I've overlooked it.) If you don't set this a browser might think you've finished sending data too early.

2) you are doing a substr of the compressed $content with the uncompressed $size. Some browsers will stop decompressing when the internal structure has an EOF marker but other browsers (Opera?) may attempt to decompress the entire downloaded buffer. That would definitely give you a 'damaged data' error. You might not be seeing this problem with small buffers because the amount of overhead and the amount of compression might exactly match.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文