ZipArchive 压缩文件时为什么生成的 zip 打不开?
常见原因是未正确调用
Dispose()或未关闭流,导致 zip 文件头不完整。.NET 的
ZipArchive依赖流的最终刷新和关闭来写入中央目录结构——如果流提前释放或未显式关闭,压缩包看似生成成功,但双击打开会提示“无法读取”或“文件损坏”。 必须使用
using语句包裹
ZipArchive和其底层
FileStream,确保资源释放顺序正确 不要手动调用
stream.Close()后再进
using,会导致重复关闭异常 若目标路径已存在同名 zip 文件,
FileMode.Create会清空它,但旧文件句柄未释放时可能引发
IOException
using (var stream = new FileStream("output.zip", FileMode.Create))
using (var archive = new ZipArchive(stream, ZipArchiveMode.Create))
{
var entry = archive.CreateEntry("hello.txt");
using (var writer = new StreamWriter(entry.Open()))
{
writer.Write("Hello from ZipArchive!");
}
} // ← 关键:两个 using 都在此结束,保证中央目录写入完成
如何向已有 zip 文件添加新文件(非覆盖)?
ZipArchiveMode.Update支持在现有 zip 中增删条目,但它要求底层流支持随机读写(如
FileStream),且不能是只读打开。直接用
FileMode.Open+
FileAccess.ReadWrite是必要前提;用
FileMode.Append或只读流会抛出
NotSupportedException。 添加同名条目会自动替换原内容;删除条目需调用
entry.Delete()(.NET 6+) 修改后必须让
ZipArchive正常 dispose,否则变更不会落盘 不建议对网络路径或 UNC 路径使用
Update模式,容易因锁竞争失败
using (var stream = new FileStream("data.zip", FileMode.Open, FileAccess.ReadWrite))
using (var archive = new ZipArchive(stream, ZipArchiveMode.Update))
{
var newEntry = archive.CreateEntry("new.log");
using (var writer = new StreamWriter(newEntry.Open()))
{
writer.WriteLine(DateTime.Now);
}
}
解压时如何避免路径遍历漏洞(如 ../web.config)?
ZipArchiveEntry.FullName可能包含恶意路径片段,直接拼接
Path.Combine(extractTo, entry.FullName)会导致文件被写到 zip 外目录。.NET 不做默认过滤,必须手动校验。 用
Path.GetRelativePath(".", entry.FullName) 无法解决,因 ..在开头时会返回绝对路径 推荐做法:用
Path.GetFileName(entry.Name)提取纯文件名,或用
Path.IsPathRooted()+
entry.FullName.Contains("..") 拒绝非法条目
更稳妥的是逐段检查 Path.GetFullPath(Path.Combine("fake", entry.FullName)) 是否仍以预期根目录开头
string extractTo = @"C:\unzip";
foreach (ZipArchiveEntry entry in archive.Entries)
{
string destinationPath = Path.GetFullPath(Path.Combine(extractTo, entry.FullName));
if (!destinationPath.StartsWith(Path.GetFullPath(extractTo) + Path.DirectorySeparatorChar))
{
throw new InvalidOperationException($"Suspicious path: {entry.FullName}");
}
entry.ExtractToFile(destinationPath, overwrite: true);
}
ZipArchive 和第三方库(如 SharpZipLib)的关键差异
ZipArchive是 .NET Framework 4.5+ / .NET Core 内置方案,轻量、无额外依赖,但功能有限:不支持 ZIP64(超 4GB 文件)、不支持密码保护、不支持分卷压缩。遇到这些需求必须换库。 压缩大文件(>2GB)时,
ZipArchive可能静默截断或抛
InvalidDataException,而 SharpZipLib 默认启用 ZIP64 需要 AES 加密?
ZipArchive完全不支持;SharpZipLib 和 DotNetZip 提供
ZipEntry.IsAesEncrypted内存敏感场景:用
ZipArchive解压时,
entry.Open()返回流不缓存全文,适合边读边处理;但压缩时所有内容必须先写入流,无法真正流式压缩
真正要稳定处理生产环境 zip,别只盯着
ZipArchive——它只是基础工具,不是万能解法。路径校验、大文件、加密、编码兼容性(如中文文件名),每个点都得单独补漏。
