CakeFest 2024: The Official CakePHP Conference

压缩过滤器

虽然 压缩封装协议 提供了在本地文件系统中创建 gzip 和 bz2 兼容文件的方法,但不代表可以在网络的流中提供通用压缩的意思,也不代表可以将一个非压缩的流转换成一个压缩流。对此,压缩过滤器可以在任何时候应用于任何流资源。

注意: 压缩过滤器产生命令行工具如 gzip 的头和尾信息。只是压缩和解压数据流中的有效载荷部分。

zlib.deflate 和 zlib.inflate

zlib.deflate(压缩)和 zlib.inflate(解压)实现了定义与 » RFC 1951 的压缩算法。 deflate 过滤器可以接受以一个关联数组传递的最多三个参数。 level定义了压缩强度(1-9)。 数字更高通常会产生更小的载荷,但要消耗更多的处理时间。 存在两个特殊压缩等级:0(完全不压缩)和 -1(zlib 内部默认值,目前是 6)。 window是压缩回溯窗口大小,以二的次方表示。 更高的值(大到 15 —— 32768 字节)产生更好的压缩效果但消耗更多内存, 低的值(低到 9 —— 512 字节)产生产生较差的压缩效果但内存消耗低。 目前默认的 window 大小是 15memory用来指示要分配多少工作内存。 合法的数值范围是从 1(最小分配)到 9(最大分配)。 内存分配仅影响速度,不会影响生成的载荷的大小。

注意: 因为最常用的参数是压缩等级,也可以提供一个整数值作为此参数(而不用数组)。

在激活 zlib 的前提下可以使用 zlib.* 压缩过滤器。

示例 #1 zlib.deflatezlib.inflate

<?php
$params
= array('level' => 6, 'window' => 15, 'memory' => 9);

$original_text = "This is a test.\nThis is only a test.\nThis is not an important string.\n";
echo
"The original text is " . strlen($original_text) . " characters long.\n";

$fp = fopen('test.deflated', 'w');
stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $params);
fwrite($fp, $original_text);
fclose($fp);

echo
"The compressed file is " . filesize('test.deflated') . " bytes long.\n";
echo
"The original text was:\n";
/* 使用 readfile 和 zlib.inflate 即时解压缩 */
readfile('php://filter/zlib.inflate/resource=test.deflated');

/* 生成的输出:

The original text is 70 characters long.
The compressed file is 56 bytes long.
The original text was:
This is a test.
This is only a test.
This is not an important string.

*/
?>

示例 #2 zlib.deflate 简单参数用法

<?php
$original_text
= "This is a test.\nThis is only a test.\nThis is not an important string.\n";
echo
"The original text is " . strlen($original_text) . " characters long.\n";

$fp = fopen('test.deflated', 'w');
/* 这里的 “6” 代表压缩级别为 6 */
stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, 6);
fwrite($fp, $original_text);
fclose($fp);

echo
"The compressed file is " . filesize('test.deflated') . " bytes long.\n";

/* 生成的输出:

The original text is 70 characters long.
The compressed file is 56 bytes long.

*/
?>

bzip2.compress 和 bzip2.decompress

bzip2.compressbzip2.decompress 工作的方式与上面讲的 zlib 过滤器相同。 bzip2.compress 过滤器接受以一个关联数组给出的最多两个参数: blocks 是从 1 到 9 的整数值,指定分配多少个 100K 字节的内存块作为工作区。 work 是 0 到 250 的整数值,指定在退回到一个慢一些, 但更可靠的算法之前做多少次常规压缩算法的尝试。调整此参数仅影响到速度, 压缩输出和内存使用都不受此设置的影响。将此参数设为 0 指示 bzip 库使用内部默认算法。 bzip2.decompress 过滤器仅接受一个参数,可以用普通的布尔值传递, 或者用一个关联数组中的 small 单元传递。 当 small 设为 &true; 值时,指示 bzip 库用最小的内存占用来执行解压缩, 代价是速度会慢一些。

在激活 bz2 支持的前提下可以使用 bzip2.* 压缩过滤器。

示例 #3 bzip2.compressbzip2.decompress

<?php
$param
= array('blocks' => 9, 'work' => 0);

echo
"The original file is " . filesize('LICENSE') . " bytes long.\n";

$fp = fopen('LICENSE.compressed', 'w');
stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE, $param);
fwrite($fp, file_get_contents('LICENSE'));
fclose($fp);

echo
"The compressed file is " . filesize('LICENSE.compressed') . " bytes long.\n";

/* 生成的输出:

The original file is 3288 bytes long.
The compressed file is 1488 bytes long.

*/
?>
add a note

User Contributed Notes 4 notes

up
9
Anonymous
8 years ago
To read a gzip encoded stream from http
<?php
$opts
= [
"http" => [
"method" => "GET",
"header" => [ "Accept-Encoding: gzip" ],
]
];
$ctx = stream_context_create($opts);
$f = fopen("http://php.net", "r", false, $ctx);
// check stream_get_meta_data($f)["wrapper_data"] has "Content-Encoding: gzip"
stream_filter_append($f, "zlib.inflate", STREAM_FILTER_READ, ["window" => 30]);
echo
stream_get_contents($f); // any stream processing
fclose($f);
up
1
Anonymous
3 years ago
To use the zlib.inflate filter with data originally written using gzcompress() or zlib.deflate, set the window option to 15 as outlined here: https://bugs.php.net/bug.php?id=68556

<?php
$fh
= fopen(file_name, 'rb');
stream_filter_append($fh, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15]);
$contents = stream_get_contents($fh);
fclose($fh);
up
2
bohwaz
5 years ago
Please note that there is currently a bug in this feature. ftell(), fseek() and fstat() functions cannot be used. Writing to a stream after using this function will not change the stream position as it should.

See bug: https://bugs.php.net/bug.php?id=49874

Also the zlib filters don't work with php://temp, php://memory and php://input streams, nothing is outputted to those streams.
up
-1
TingSong
1 year ago
To decompress a gzipped stream:

<?php
$stream
= fopen('https://example.com/some/file.txt.gz', 'rb');
stream_filter_append($stream, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15+16]);

// read the decompressed line directly
$line = fgets($stream);

// process the lines
?>

As the doc of zlib https://www.zlib.net/manual.html#Advanced

The 'window' parameter between 8 and 15 specified the window size from 2⁸ to 2¹⁵ bytes. It can be added by 16 for wrapping with gzip header and trailer instead of zlib wrapper.

And, window could be -8..-15 for unwrapping RAW deflate data.
To Top