如何用stream filter直接解压gzip格式的压缩流

PHP 提供了流过滤器功能,可以直接在流的读写过程中透明地进行加密、压缩、计算校验和等操作。然而压缩过滤器的官方文档上只介绍了 zlib.deflatezlib.inflate 两个过滤器可以处理 zlib 压缩的数据流,没有提供直接处理 gzip 格式压缩流的方法。

上一篇文章中我分享了使用 DEFLATE 算法的三种压缩格式,可以知道 zlib 和 gzip 使用的都是 DEFLATE 数据压缩算法,只是头尾长度和内容不同而已。这里我再分享一下直接用 zlib 过滤器处理 gzip 压缩流的方法,毕竟流过滤器有着不必接触原始文件,直接进行数据处理的优势。

方法非常简单,只要在挂载 zlib 流过滤器时,提供参数 window=31 即可。以下是范例代码,我们从 S3 中读取 gzip 压缩过的 CSV 文件流:

$stream = $s3_client->getReadStream('somefile.csv.gz');
stream_filter_append($stream, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 31]);
$line = fgets($stream);
if (trim($line) !== '') {
    $fields = str_getcsv($line);
    // ...
}

根据 PHP 的官方文档,这个 window 参数的值应该在 8 到 15 之间才对,表示压缩时的窗口大小为 28 到 215 之间,为什么可以指定为 31 呢?这可以算是 zlib 库的一种高级用法。根据 zlib 库的文档windowBit 可以介于 8..15,意思是窗口大小在28 到 215 之间,也可以指定 -8..-15 表示要输出没有 zlib 封装的 DEFLATE 原始数据,或者还可以加上 16 来改用 gzip 封装格式。zlib 库中的 windowBit 正好对应了 PHP stream_filter_append$params 参数里的 window 项目。

可能是因为 zlib 文档中声明这些高级用法是只针对当前版本有效,所以 PHP 官方文档没有将这些特殊值收录进来。不过至少当前版本我们可以放心使用,可以在 composer.json 中的 platform 部分声明一下兼容的 ext-zlib 版本为 ^1.0,这样版本有变化的时候,composer 会给予提示。

标签: gzip, stream, filter, zlib

添加新评论