使用 ziparchive 更新 zip 文件必须指定 ziparchivemode.update,底层流需可读写,手动删除同名旧条目后再创建新条目,写入后须调用 flush() 和 dispose(),路径分隔符必须为正斜杠 /。

用 ZipArchive 打开 ZIP 时必须指定 ZipArchiveMode.Update
默认的 ZipArchive 构造方式(如只传 Stream)是只读模式,直接调用 CreateEntry 会抛出 NotSupportedException。必须显式传入 ZipArchiveMode.Update,且底层流不能是只读的(比如 File.OpenRead() 就不行)。
- 使用
File.Open(..., FileMode.Open, FileAccess.ReadWrite)或File.Open(..., FileMode.OpenOrCreate, FileAccess.ReadWrite) - 如果 ZIP 文件不存在,
FileMode.Open会抛FileNotFoundException,建议先检查文件是否存在,再选OpenOrCreate - 别用
MemoryStream做更新目标——它不支持随机写入,ZipArchiveMode.Update会失败
ZipArchive.CreateEntry 会覆盖同名文件,但不会自动删除旧条目
ZIP 是基于条目(entry)的扁平结构,没有“目录”概念;添加同名路径的 entry 时,CreateEntry 不报错,但后续写入会覆盖该路径的内容。但注意:原 ZIP 中已存在的同名 entry 并不会被自动清理——实际效果取决于 ZIP 工具如何解析重复路径(多数解压工具取最后一个)。真正安全的更新需手动删除旧 entry。
- 遍历
archive.Entries,用entry.FullName.Equals("path/in/zip.txt", StringComparison.OrdinalIgnoreCase)查找 - 调用
entry.Delete()(仅在Update模式下可用) - 再
CreateEntry+ 写入新内容 - 注意:.NET 6+ 支持
Delete();.NET 5 及更早版本不支持,只能重建整个 ZIP
写入内容后必须显式调用 stream.Flush() 和 archive.Dispose()
ZipArchive 的更新是延迟写入的:entry 创建、流写入都不立即落盘。只有在 Dispose()(或 using 块结束)时才真正重写 ZIP 文件头和中央目录。漏掉这步会导致 ZIP 损坏或新增文件不可见。
- 务必用
using (var archive = new ZipArchive(stream, ZipArchiveMode.Update)) { ... } - 不要提前
stream.Close()或stream.Dispose(),否则archive.Dispose()会失败 - 如果手写
try/finally,确保archive.Dispose()在finally中执行
追加文件时路径分隔符必须用正斜杠 /
ZIP 规范要求内部路径使用 /,即使在 Windows 上用 Path.Combine 生成的是 ,也会导致解压时路径异常(如创建出名为 "folderile.txt" 的单个文件,而非子目录结构)。
- 用
entryName.Replace("\", "/")或Path.GetRelativePath(root, fullPath).Replace("\", "/") - 避免以
/开头(如/data/file.txt),ZIP 解析器可能忽略前导斜杠,导致路径错位 - 空目录无法直接添加——ZIP 没有目录 entry,只能靠文件路径隐含,例如
"logs/"不合法,但"logs/empty.txt"可以让解压工具创建logs目录
SharpZipLib 或 DotNetZip 这类库——它们暴露更多控制点,但代价是引入第三方依赖。










