PHP Conference Japan 2024

壓縮過濾器

雖然壓縮包裝器提供了一種在本地檔案系統上建立 gzip 和 bz2 相容檔案的方法,但它們不提供透過網路串流進行廣義壓縮的方法,也不提供從非壓縮串流轉換到壓縮串流的方法。為此,可以隨時將壓縮過濾器應用於任何串流資源。

注意壓縮過濾器不會產生命令列工具(例如 gzip)使用的標頭和尾端。它們只壓縮和解壓縮壓縮資料串流的有效負載部分。

zlib.deflate 和 zlib.inflate

zlib.deflate(壓縮)和 zlib.inflate(解壓縮)是 » RFC 1951 中描述的壓縮方法的實現。deflate 篩選器最多接受三個以關聯陣列形式傳遞的參數。level 描述要使用的壓縮強度(1-9)。數字越大通常會產生較小的有效負載,但會增加處理時間。還有兩個特殊的壓縮級別:0(完全不壓縮)和 -1(zlib 內部預設值 - 目前為 6)。window 是壓縮回環視窗大小的以 2 為底的對數。值越高(最高 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
"原始文字長度為 " . strlen($original_text) . " 個字元。\n";

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

echo
"壓縮後的檔案大小為 " . filesize('test.deflated') . " 位元組。\n";
echo
"原始文字為:\n";
/* 使用 readfile 和 zlib.inflate 即時解壓縮 */
readfile('php://filter/zlib.inflate/resource=test.deflated');

/* 產生輸出:

原始文字長度為 70 個字元。
壓縮後的檔案大小為 56 位元組。
原始文字為:
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
"原始文字長度為 " . strlen($original_text) . " 個字元。\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
"壓縮後的檔案大小為 " . filesize('test.deflated') . " 位元組。\n";

/* 產生輸出:

原始文字長度為 70 個字元。
壓縮後的檔案大小為 56 位元組。

*/
?>

bzip2.compress 與 bzip2.decompress

bzip2.compressbzip2.decompress 的運作方式與上述 zlib 過濾器相同。bzip2.compress 過濾器最多接受兩個參數,以關聯陣列的元素形式提供:blocks 是一個介於 1 到 9 之間的整數值,指定要為工作區配置的 100kbyte 記憶體區塊數量。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
"原始檔案大小為 " . filesize('LICENSE') . " 位元組。\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
"壓縮後的檔案大小為 " . filesize('LICENSE.compressed') . " 位元組。\n";

/* 產生輸出:

原始檔案大小為 3288 位元組。
壓縮後的檔案大小為 1488 位元組。

*/
?>
新增註釋

使用者貢獻的註釋 4 則註釋

匿名
9 年前
從 http 讀取 gzip 編碼的串流
<?php
$opts
= [
"http" => [
"method" => "GET",
"header" => [ "Accept-Encoding: gzip" ],
]
];
$ctx = stream_context_create($opts);
$f = fopen("https://php.dev.org.tw", "r", false, $ctx);
// 檢查 stream_get_meta_data($f)["wrapper_data"] 是否包含 "Content-Encoding: gzip"
stream_filter_append($f, "zlib.inflate", STREAM_FILTER_READ, ["window" => 30]);
echo
stream_get_contents($f); // 任何串流處理
fclose($f);
匿名
3 年前
要使用 zlib.inflate 過濾器處理原本使用 gzcompress() 或 zlib.deflate 寫入的資料,請將 window 選項設定為 15,如此處所述: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);
bohwaz
6 年前
請注意,此功能目前存在錯誤。 無法使用 ftell()、fseek() 和 fstat() 函式。 使用此函式後寫入串流不會像預期那樣更改串流位置。

請參閱錯誤:https://bugs.php.net/bug.php?id=49874

此外,zlib 過濾器不適用於 php://temp、php://memory 和 php://input 串流,不會將任何內容輸出到這些串流。
TingSong
1 年前
要解壓縮 gzip 壓縮的串流

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

// 直接讀取解壓縮後的行
$line = fgets($stream);

// 處理這些行
?>

如 zlib 文件 https://www.zlib.net/manual.html#Advanced 所述

「window」參數介於 8 到 15 之間,指定了從 2⁸ 到 2¹⁵ 位元組的視窗大小。可以加上 16,使用 gzip 標頭和尾部進行包裝,而不是 zlib 包裝器。

此外,window 可以是 -8..-15,用於解開 RAW deflate 資料。
To Top