ziparchive是php处理zip文件的唯一可靠选择,addfile()不支持目录需手动遍历,extractto()存在路径穿越风险须校验,中文乱码因zip未声明utf-8编码,setpassword()对aes加密无效。

ZipArchive 是 PHP 处理 ZIP 文件的唯一可靠选择,其他方式如 gzencode() 或手动拼 ZIP 流,无法保留多文件结构和目录层级,双击打不开、解压失败是常态。
addFile() 压目录为什么没反应?
addFile() 只接受真实文件路径,传入目录会静默失败或报 ZipArchive::addFile(): File not found。它不递归,也不报错——这是最常被忽略的逻辑盲点。
- 必须用
RecursiveDirectoryIterator+RecursiveIteratorIterator手动遍历 - 对每个
SplFileInfo实例调用isFile()过滤掉.、..、符号链接、目录项 - 计算相对路径:用
substr($file->getPathname(), strlen($srcDir) + 1),否则所有文件都堆在 ZIP 根目录下 - 显式跳过
.git、.env、.htaccess等敏感隐藏文件,避免泄露
extractTo() 解压上传的 ZIP 包有多危险?
extractTo() 完全不做路径校验,遇到 ../../etc/passwd 这类条目,会直接写到系统关键位置。这不是理论风险,而是真实发生过的高危漏洞。
- 绝对不要对不可信来源(如用户上传)的 ZIP 包直接调用
extractTo() - 正确做法:循环
numFiles,对每个getNameIndex($i)结果做路径净化 - 用
realpath()展开后,再用0 === strpos($realPath, $allowedBase)判断是否落在白名单目录内 - 遇到含
..、\0、C:\的文件名,直接continue或throw
中文文件名乱码不是编码问题,是 ZIP 元数据没设 UTF-8
显示为???.txt 或一堆方块,90% 情况下不是 PHP 文件编码错了,而是 ZIP 包本身没声明 UTF-8 编码。
- PHP ZipArchive 不支持强制 UTF-8 标志
- 升级到 PHP ≥ 8.0 是最稳妥解法;若不能升级,需改用
exec('zip -u -UN=UTF8 ...')调用系统命令 - 更常见的是 zip 扩展根本没启用:
extension_loaded('zip')返回false就得先装php-zip(Ubuntu)或重编 PHP(macOS)
setPassword() 为什么解不开带密码的 ZIP?
setPassword() 方法在绝大多数生产环境中无效:只对传统 ZipCrypto 加密部分有效,对 AES-256 加密完全不支持,且依赖极少数编译选项。
- 别在代码里写
$zip->setPassword('123')然后期待它工作 - 必须处理密码 ZIP?老实用
exec()调用系统unzip:- 用
escapeshellarg()包裹密码和文件路径 - 检查返回码:
$returnCode !== 0表示密码错、损坏或磁盘满 - 注意
unzip在不同系统行为差异(如 macOS 的ditto不支持 -P)
- 用
复杂点不在 API 调用本身,而在路径净化、编码元数据、扩展可用性这三处——它们不出错时不显眼,一出就是空包、越权写入、乱码或静默失败。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











