在PHP中,如何解压缩两次压缩的文件?

时间:2014-07-29 16:05:36

标签: php gzip zlib

我有一个很大的bigfile.gz.gz文件。我想在运行中解压缩它。理想情况下,这就是我的想法:

$in = fopen('compress.zlib://compress.zlib://bigfile.gz.gz', 'rb');
while (!feof($in))
    print fread($in, 4096);
fclose($in);

但是,compress.zlib://不能以这种方式链接:

PHP Warning:  fopen(): cannot represent a stream of type ZLIB as a File Descriptor in gztest.php on line 1

所以我以为我会将gzopen()compress.zlib://合并在一起:

$in = gzopen('compress.zlib://bigfile.gz.gz', 'rb');
while (!gzeof($in))
    print gzread($in, 4096);
gzclose($in);

但是,这只会解压缩一个级别的gzip。

我尝试了其他10种方法,遗憾的是gzopen()如果使用php://memory编写了fwrite(),则无法使用stream_filter_append(… zlib.inflate …)。并且$in = popen('zcat bigfile.gz.gz | gunzip', 'rb'); while (!feof($in)) print fread($in, 4096); fclose($in); 无法读取gzip压缩文件。

这是我能想到的最好的,但它会产生两个系统进程,这会产生不良的开销:

{{1}}

有人可以提出更好的建议吗?

1 个答案:

答案 0 :(得分:2)

可以使用zlib.inflate过滤器解压缩.gz文件。你只需要先删除gzip头。要动态执行此操作,您必须部署自定义过滤器:

<?php

class gzip_header_filter extends php_user_filter {

    private $filtered = 0;

    public function filter($in, $out, &$consumed, $closing) {
        while ($bucket = stream_bucket_make_writeable($in)) {
            if($this->filtered == 0) {
                $header_len = 10;
                $header = substr($bucket->data, 0, 10);
                $flags = ord($header[3]);
                if($flags & 0x08) {
                    // a filename is present
                    $header_len = strpos($bucket->data, "\0", 10) + 1;
                } 
                $bucket->data = substr($bucket->data, $header_len);
                $this->filtered = $header_len;
            }
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}

stream_filter_register('gzip_header_filter', 'gzip_header_filter');

$in = fopen('bigfile.gz.gz', 'rb');
stream_filter_append($in, 'gzip_header_filter', STREAM_FILTER_READ);
stream_filter_append($in, 'zlib.inflate', STREAM_FILTER_READ);
stream_filter_append($in, 'gzip_header_filter', STREAM_FILTER_READ);
stream_filter_append($in, 'zlib.inflate', STREAM_FILTER_READ);

while (!feof($in))
    print fread($in, 4096);
fclose($in);

?>

请注意,上面的代码不处理可以存储在gz文件中的注释和其他额外数据。

相关问题