正确做法是用 ziparchive 构造函数传入未缓冲、只读的 filestream(filemode.open + fileaccess.read + fileshare.read),指定 ziparchivemode.read 且不访问 entries 属性,改用 getentry 按需获取条目,并做好异常处理与资源释放。

用 ZipArchive 配合 Stream 打开 ZIP,避免内存爆掉
大型 ZIP(比如几个 GB)如果用 ZipFile.ExtractToDirectory 或直接调 archive.Entries 全量加载,会把所有条目元数据一次性读进内存,还可能触发内部缓冲膨胀。正确做法是用 ZipArchive 构造函数传入一个未缓冲、只读的 FileStream,并指定 LeaveOpen = false(或手动管理生命周期)。
关键点:
-
FileStream必须以FileMode.Open+FileAccess.Read+FileShare.Read打开,不能用File.ReadAllBytes或MemoryStream包裹 - 构造
ZipArchive时传ZipArchiveMode.Read,且不要访问Entries属性——它会强制扫描整个中央目录,对超大 ZIP 可能卡住或 OOM - 改用
archive.GetEntry(string)按需获取单个条目,或遍历archive.Entries前先确认 ZIP 不含恶意超长路径/重复条目(见下一条)
遍历条目前先做轻量校验,防止 ZIP 中央目录解析失败
某些损坏或非标准 ZIP(如分卷后拼接、加密头残留)在调 archive.Entries 时会抛 InvalidDataException 或卡死。别等 foreach 才发现——先用 ZipArchive 的底层流定位到中央目录末尾,读 4 字节签名 0x06054b50 确认结构可读;更稳妥的做法是捕获 IOException 和 InvalidDataException,并在 catch 块中 dispose archive 和 stream。
常见陷阱:
- 没加 try/finally,导致
FileStream或ZipArchive未释放,后续无法删除或重命名源 ZIP 文件 - 误把
archive.Entries.Count当作文件数——它实际是中央目录条目数,ZIP 允许重复名或空条目,真实有效文件需过滤entry.Length > 0 && !entry.FullName.EndsWith("/") - 未处理 ZIP 中的 Unicode 路径(
General Purpose Bit 11),导致entry.FullName出现乱码;.NET 6+ 默认支持,但旧版本需手动设置UseUnicode = true(仅限创建,读取依赖 ZIP 自身标志)
逐个解压文件到磁盘或内存,控制缓冲区大小防卡顿
对每个 ZipArchiveEntry,调 entry.Open() 得到一个只读流,再用小缓冲区(如 8KB)复制到目标 FileStream 或 MemoryStream。别用 entry.ExtractToFile——它内部用的是 64KB 缓冲且不暴露进度,对超大单文件(如 ZIP 内含 2GB SQL 备份)容易假死。
实操建议:
- 解压前检查
entry.Length,跳过长度为 0 的条目(可能是目录占位符) - 写目标文件前确保父目录存在:
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)) - 若需监控进度,用
Stream.CopyToAsync(dest, bufferSize, cancellationToken)配合IProgress<long></long>报告已复制字节数 - 特别注意:ZIP 中的“目录”条目(
FullName以/结尾)没有entry.Open()流,直接 skip 即可
遇到 ZIP64 或超过 65535 个文件时的兼容性提醒
.NET 的 ZipArchive 默认支持 ZIP64(即单文件 >4GB 或总文件数 >65535),但前提是 ZIP 文件本身正确写入了 ZIP64 扩展头。如果用老旧工具生成的 ZIP 声称支持 ZIP64 却漏写扩展字段,ZipArchive 会静默截断条目列表——现象是 Entries 只返回前 65535 个,且无异常。
验证方式:
- 用命令行
7z l -slt your.zip | findstr "ZIP64"看是否标记ZIP64 extra field: Yes - 代码中可尝试读最后一个条目:
archive.Entries.LastOrDefault(),若返回 null 但archive.Entries.Count == 65535,高度怀疑 ZIP64 兼容问题 - 此时应换用
SharpZipLib并启用Zip64 = Zip64Option.Always,它对 ZIP64 边界处理更鲁棒
真正棘手的不是怎么读,而是有些 ZIP 根本没写中央目录偏移——比如流式生成中途断电,这种文件连 ZipArchive 构造都会失败,必须靠 hex editor 手动定位局部文件头再逐个解析,超出常规解压范畴。










