当前位置: 首页> 最新文章列表> 使用 zip_read() 对加密 Zip 文件无效的问题说明

使用 zip_read() 对加密 Zip 文件无效的问题说明

M66 2025-06-05

在 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() 设计上并不支持加密文件的读取。

三、正确的做法:使用 ZipArchive 类的密码解密功能

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()