PHP ZipArchive需确认扩展启用、中文名用addFromString避免乱码、解压前校验路径防遍历、大文件用流式添加或系统命令。

PHP 原生 ZipArchive 类能可靠完成 zip 压缩与解压,但默认不启用、路径权限易出错、中文文件名处理需手动干预——不是调个方法就能跑通。
确认 ZipArchive 扩展已启用且可用
很多报错如 Class 'ZipArchive' not found 或 ZipArchive::open(): Invalid or uninitialized Zip object 都源于扩展未加载。别只看 phpinfo() 里有没有,要实际测试:
- 运行
php -m | grep zip(命令行)或extension_loaded('zip')(脚本内)确认返回 true - 若用 Docker,确保
Dockerfile中有docker-php-ext-install zip且未被后续disable_functions拦截 - 共享主机常禁用
zip_open等底层函数,此时ZipArchive会静默失败,建议加if (!$zip->open($file, ZipArchive::CREATE) === TRUE)显式判断
压缩文件时路径和中文名怎么处理才不乱码
Windows 下用 PHP 打包的 zip,解压到 Windows 资源管理器常出现中文名变问号或乱码,本质是 ZipArchive 默认用 CP437 编码写入文件名,而现代系统期望 UTF-8。解决逻辑分两层:
- Linux/macOS 服务器:直接用 UTF-8 路径调用
$zip->addFile($realpath, $basename)即可,$basename必须是 UTF-8 字符串(确保源文件名本身没被转码破坏) - Windows 服务器或需兼容旧版解压工具:必须用
iconv('UTF-8', 'GB18030', $filename)或mb_convert_encoding($filename, 'GB18030', 'UTF-8')转为 GBK 编码再传入addFile();但注意这会导致 Linux 解压时显示异常,所以更推荐统一用addFromString()+ 手动构造文件头(见下条) - 稳妥方案:避免直接 addFile,改用
$zip->addFromString('中文名.txt', file_get_contents($path)),绕过文件系统编码转换环节
解压 zip 时如何防止路径遍历攻击(…/…/etc/passwd)
ZipArchive::extractTo() 若不校验内部文件路径,攻击者可构造恶意 zip 包把文件释放到任意目录。关键不是“能不能解”,而是“敢不敢放”:
立即学习“PHP免费学习笔记(深入)”;
- 必须对每个
$zip->getNameIndex($i)返回的路径做白名单检查:!str_starts_with($name, '/') && !str_contains($name, '..') && $name === ltrim($name, './') - 更安全做法:用
realpath()拼接目标目录后,检查解压后路径是否仍落在该目录内:$realTarget = realpath($dest . '/' . $name); if (strpos($realTarget, realpath($dest)) !== 0)则跳过 - 注意
extractTo()不支持回调过滤,必须先遍历所有条目(numFiles+getNameIndex),逐个校验后再extractTo()或用getFromIndex()+file_put_contents()手动写入
大文件压缩卡死或内存溢出怎么办
ZipArchive 默认将整个压缩包载入内存,单个 200MB 文件就可能触发 Allowed memory size exhausted。这不是配置调大就能解决的临时问题:
- 用
ZipArchive::CREATE | ZipArchive::OVERWRITE模式打开,配合addFile()分批添加(每次 add 后内存不会累积暴涨) - 避免一次性
file_get_contents()读大文件,改用addFromString($name, stream_get_contents(fopen($file, 'r')))流式读取 - 真正超大文件(>500MB)应考虑用系统命令:
exec("zip -r {$zipFile} {$sourceDir} 2>&1", $output, $returnCode),并设置set_time_limit(0)和ini_set('memory_limit', '-1')
最麻烦的不是语法,是 zip 格式本身在不同平台、不同解压工具间的编码和路径解析差异。哪怕 ZipArchive 方法全对,一个没校验的 ../ 或一个没转码的中文名,就足以让整个功能在线上环境失效。



















