在 PHP 中,操作 Zip 文件时,我们经常会用到 zip_read() 函数,它属于 ZipArchive 类的一部分,主要用来遍历和读取 Zip 包中的文件条目。然而,当遇到加密的 Zip 文件时,很多开发者发现使用 zip_read() 读取会失败或根本无法访问加密内容。那么这是为什么呢?本文将结合 PHP 的 Zip 扩展机制,详细解析这一问题的原因及其解决思路。
$zip = zip_open('encrypted.zip');
if ($zip) {
while ($entry = zip_read($zip)) {
echo zip_entry_name($entry) . "\n";
if (zip_entry_open($zip, $entry)) {
echo zip_entry_read($entry, zip_entry_filesize($entry));
zip_entry_close($entry);
}
}
zip_close($zip);
}
上述代码在读取普通 Zip 文件时能够正常工作,但在遇到加密的 Zip 文件时,常出现如下问题:
无法读取文件内容,zip_entry_read() 返回空字符串或错误。
甚至无法遍历到任何文件条目,表现为 zip_read() 返回 false。
zip_read() 属于 PHP 的 libzip 库的旧接口(基于 ZIP 文件的低层操作),而且原生并不支持对加密文件内容的解密。加密的 Zip 文件(通常使用密码保护)采用的是对文件数据进行加密的方式,数据不是简单明文存储。
zip_read() 只能访问 Zip 文件中的文件条目和未加密的文件内容。
如果 Zip 文件加密了,Zip 扩展必须通过提供正确的密码,调用对应的解密逻辑才能正确读取文件。
PHP 的 zip_read() 并没有提供密码参数,也不支持自动解密。
简言之,zip_read() 设计上并不支持加密文件的读取。
PHP 从 7.2 版本开始的 Zip 扩展提供了更完整的 ZipArchive 类,支持设置密码读取加密文件:
$zip = new ZipArchive();
$res = $zip->open('encrypted.zip');
if ($res === true) {
// 设置密码
$zip->setPassword('your_password');
for ($i = 0; $i < $zip->numFiles; $i++) {
$stat = $zip->statIndex($i);
echo "File: " . $stat['name'] . "\n";
$stream = $zip->getStream($stat['name']);
if (!$stream) {
echo "Failed to open stream for {$stat['name']}\n";
continue;
}
$contents = stream_get_contents($stream);
fclose($stream);
echo $contents;
}
$zip->close();
} else {
echo "Failed to open zip file\n";
}
重点说明:
setPassword() 方法用于提供解密所需的密码。
使用 getStream() 可以获取加密文件的内容流,前提是密码正确。
这种方法可以正常读取加密的 Zip 文件内容。
zip_read() 是 PHP 早期的 Zip 操作接口,不能处理加密文件。
加密的 Zip 文件必须使用支持密码解密的接口,如 ZipArchive。
通过 ZipArchive::setPassword(),PHP 可以成功打开并读取加密文件。
在实际开发中,遇到加密 Zip 文件推荐使用 ZipArchive 类,避免使用过时的 zip_read()。