在使用 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 文件的读取变得更加安全、稳定、高效。