ziparchive 是 php 原生解压 zip 最稳方案,需检查 open() 返回值、处理中文编码、避免内存溢出、分批添加大文件,并通过重建实现删除。

用 ZipArchive 解压 ZIP 文件最稳
PHP 原生操作 ZIP,ZipArchive 是唯一靠谱选择。别碰 exec('unzip') 或第三方库——权限、路径、编码问题一堆,而且不跨平台。
常见错误是没检查 open() 返回值,直接调 extractTo(),结果静默失败。Windows 下路径分隔符、中文文件名乱码也常在这儿爆。
-
ZipArchive::open()必须判断返回值:0 才成功,其他值对应不同错误(如ZIPARCHIVE::ER_NOZIP表示不是 ZIP 文件) - 解压前用
$zip->numFiles确认有内容,避免空包导致extractTo()创建空目录 - 目标路径末尾加
/,否则extractTo('path')可能误把path当作文件名处理 - 中文文件名需在
open()前调setEncoding('UTF-8')(PHP 8.2+),老版本只能靠mb_convert_encoding()临时转
往 ZIP 包里塞文件,addFile() 和 addFromString() 别混用
要加磁盘上已有文件,用 addFile();要加动态生成的内容(比如日志文本、JSON 字符串),用 addFromString()。混用会导致路径错乱或内容截断。
容易踩的坑是传给 addFile() 的路径没校验存在性,或用了相对路径但工作目录变了;而 addFromString() 的第二个参数(归档内路径)如果含 ../,部分 PHP 版本会拒绝写入(安全限制)。
立即学习“PHP免费学习笔记(深入)”;
-
addFile('/var/log/app.log', 'logs/app.log')—— 第一个参数是真实路径,第二个是 ZIP 内部路径 -
addFromString('{"status":"ok"}', 'data.json')—— 内容直接进 ZIP,不经过磁盘 - 批量添加时,别在循环里反复
open()/close(),先open(),加完再close() - 大文件(>10MB)用
addFile()比addFromString(file_get_contents())内存更省
ZipArchive::ER_MEMORY 错误基本等于内存不够
压缩大量小文件或单个超大文件时,ZipArchive::open() 或 close() 报 ER_MEMORY,不是 ZIP 本身问题,是 PHP 内存耗尽了。
这个错误不会抛异常,只返回整数错误码,所以必须显式检查。默认 memory_limit 经常卡在 128M,而 ZIP 操作中间缓存可能翻倍占用。
- 用
ini_set('memory_limit', '512M')临时提限(仅当前请求) - 避免一次性
addFile()几千个文件,改用分批 +flush()(PHP 8.0+ 支持) - 确认
zlib扩展已启用——没它ZipArchive功能不全,某些压缩级别直接报错 - CLI 模式下
memory_limit常为 -1,但 Web 模式下通常受限,别假设一致
删除 ZIP 内文件只能重建,没有“就地删”
ZipArchive 不提供 delete() 方法。想删掉某个成员?只能打开原包 → 遍历读出不需要的文件 → 新建 ZIP → 逐个 addFile() 或 addFromString() → 写回。
这听着麻烦,但比试图用 fseek() 手动修补 ZIP 结构靠谱得多——ZIP 格式本身不支持随机删,强行操作大概率损坏包。
- 用
$zip->locateName('old.txt')先确认文件是否存在,避免遍历时漏判 - 重建时注意时间戳:默认新文件时间是当前时间,如需保留原时间,得用
$zip->statName()取mtime再传给addFile()的可选参数 - 不要用
rename()覆盖原文件,先写新 ZIP 到临时路径,unlink()原包后再rename(),防止中间出错丢数据
ZIP 操作真正的复杂点不在语法,而在路径语义、编码边界和内存模型——这些地方不显式控制,跑一次成功不代表下次还行。











