ZipFile.ExtractToDirectory是最直接的整包解压方法,但需确保目标目录已存在、源ZIP可读且不被占用,不支持ZIP64超大文件(>4GB)及非UTF-8编码;复杂需求应改用ZipArchive手动控制。
用 ZipFile.ExtractToDirectory 最快解压到文件夹
只要目标是把整个 zip 包原样解压到一个目录,zipfile.extracttodirectory 是最直接的选择。它底层调用的是 windows 的压缩 api(.net core 3.0+ 和 .net 5+ 默认走托管实现),不依赖外部工具,也不需要手动遍历条目。
常见错误是传入不存在的目标目录路径——它不会自动创建父目录,会直接抛 DirectoryNotFoundException。另外,如果目标目录非空且存在同名文件,默认会覆盖,没有提示。
- 确保目标目录已存在:
Directory.CreateDirectory(@"C:\output") - 源 ZIP 路径必须可读,且不能被其他进程占用(比如正被资源管理器预览)
- 不支持 ZIP64 扩展的超大文件(>4GB)在旧版 .NET Framework 中可能失败;.NET Core 2.1+ 已支持
- 示例:
ZipFile.ExtractToDirectory(@"C:\data.zip", @"C:\unzipped");
需要控制解压行为?改用 ZipArchive + 手动遍历
当你要跳过某些文件、重命名条目、校验 CRC、或只解压匹配特定模式的文件时,ZipArchive 是唯一靠谱的方式。它暴露了每个 ZipArchiveEntry 的原始信息,包括 FullName、Length、IsDirectory 等。
容易踩的坑是忽略路径遍历风险:ZIP 文件里可以包含 ..\..\etc\passwd 这类恶意路径。.NET 不做默认过滤,你得自己清理 entry.FullName。
- 用
Path.GetRelativePath或正则剔除..段(推荐用Path.GetFullPath拼接后比对是否仍在目标目录内) -
entry.Open()返回流,记得用using包裹,否则句柄泄漏 - 解压单个条目前,先用
Directory.CreateDirectory创建其所在子目录 - 示例片段:
using var archive = ZipFile.OpenRead(@"C:\data.zip"); foreach (var entry in archive.Entries.Where(e => e.Name.EndsWith(".txt"))) { var destPath = Path.Join(@"C:\target", entry.FullName); Directory.CreateDirectory(Path.GetDirectoryName(destPath)); entry.ExtractToFile(destPath, overwrite: true); }
遇到 InvalidDataException 或乱码文件名?检查编码和 ZIP 版本
中文文件名解压后变成 ???.txt,或者直接抛 InvalidDataException: End of Central Directory record could not be found,大概率是 ZIP 包用了非 UTF-8 编码(比如 GBK/Shift-JIS)生成,而 .NET 默认按 UTF-8 解析目录结构。
.NET 6+ 提供了 ZipArchive 构造函数的 encoding 参数,但仅限于读取——写入仍不支持指定编码。老版本(.NET Framework / .NET Core 3.1)基本无解,只能换库。
- .NET 6+ 可显式传
Encoding.GetEncoding("GBK"):using var archive = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: true, encoding: Encoding.GetEncoding("GBK")); - 若用第三方库,
SharpZipLib对传统 ZIP 编码兼容更好,但需自行处理安全路径校验 - Windows 自带的“发送到 → 压缩文件夹”生成的 ZIP 通常没问题;7-Zip 默认用 UTF-8,但勾选“使用传统编码”就会出问题
大文件解压卡住或内存暴涨?别让 ExtractToDirectory 一次加载全部
ZipFile.ExtractToDirectory 在解压前会扫描整个 ZIP 的中央目录,对几百 MB 以上的 ZIP 没问题,但遇到上 GB、成千上万个条目的 ZIP 时,可能明显卡顿——不是性能瓶颈,而是它内部做了完整元数据预加载。
这时候回到 ZipArchive 流式读取更可控:它只在调用 entry.Open() 时才真正解压对应条目,内存占用稳定在几 MB 级别。
- 避免对
archive.Entries做.ToList()或.Count(),这会强制加载全部元数据 - 用
foreach直接迭代archive.Entries,它是延迟执行的 - 如果只是提取单个关键文件(如
config.json),找到即break,不用遍历完


















