在使用PHP 的Zip擴展處理ZIP 壓縮包時, zip_read()和zip_entry_read()是兩個經常一起配合使用的函數。然而,很多開發者在實際使用時,遇到了一個令人困惑的問題:用zip_read()迭代條目,再通過zip_entry_read()讀取內容時,會報錯,甚至返回空內容或亂碼。這是什麼原因導致的?本文將深入分析原因並給出解決方案。
考慮下面這段代碼:
$zip = zip_open('http://m66.net/test.zip');
if (is_resource($zip)) {
while ($entry = zip_read($zip)) {
echo '正在讀取文件: ' . zip_entry_name($entry) . PHP_EOL;
if (zip_entry_open($zip, $entry)) {
$contents = zip_entry_read($entry);
echo $contents . PHP_EOL;
zip_entry_close($entry);
}
}
zip_close($zip);
}
運行上述代碼時,有可能會出現如下錯誤:
Warning: zip_entry_read(): supplied resource is not a valid Zip Entry resource in...
或者讀取到的內容為空,完全不符合預期。
這個問題的根源通常是以下幾點之一:
儘管zip_open()支持傳入一個文件路徑,但它不一定支持遠程URL 。如上例中使用的是http://m66.net/test.zip ,這取決於你的PHP 是否啟用了allow_url_fopen ,以及底層的ZIP 擴展是否支持從流資源正確讀取ZIP 數據。大多數情況下, zip_open()要求是一個可直接seek 的本地文件。
zip_entry_read()需要明確指定讀取的長度。如果不指定長度,它會默認讀取1024 字節,這在文件較大時會導致內容不完整;而如果文件為空或位置不正確,則可能直接失敗。
使用zip_entry_open()打開條目時,如果失敗,隨後的zip_entry_read()將無法執行。此外,某些版本的PHP 在多個條目間切換讀取時存在兼容性問題,尤其是在壓縮方式非STORE 或DEFLATE 的情況下。
為確保ZIP 條目能正確讀取,應遵循以下步驟:
$localFile = 'test.zip';
copy('http://m66.net/test.zip', $localFile); // 將遠程文件下載到本地
$zip = zip_open($localFile);
if (is_resource($zip)) {
while ($entry = zip_read($zip)) {
$name = zip_entry_name($entry);
echo "讀取文件: $name" . PHP_EOL;
if (zip_entry_open($zip, $entry)) {
$size = zip_entry_filesize($entry);
$contents = zip_entry_read($entry, $size);
echo "內容: " . PHP_EOL . $contents . PHP_EOL;
zip_entry_close($entry);
} else {
echo "無法打開條目: $name" . PHP_EOL;
}
}
zip_close($zip);
}
這裡有幾個關鍵點:
先下載遠程ZIP 到本地,再用zip_open()打開本地文件;
使用zip_entry_filesize()獲取正確的讀取長度;
始終檢查zip_entry_open()是否返回true 。
使用zip_read()報錯的主要原因,通常是資源打開方式不正確、未正確指定讀取長度或遠程路徑處理不當。建議始終使用本地文件路徑,並配合zip_entry_filesize()精確讀取內容。如果有更高的需求,也可以考慮使用ZipArchive類,它提供了更高級、穩定的接口。
通過正確的流程,可以讓ZIP 文件的讀取變得更加安全、穩定、高效。