zipfile.createfromdirectory 无法保留多级目录相对路径,因其仅以源目录为基准计算路径且不支持裁剪前缀;需用 zipoutputstream 手动设置 zipentry.name 为相对于逻辑基准的路径(反斜杠转正斜杠、目录结尾加/),并显式添加空目录条目。

直接用 ZipFile.CreateFromDirectory 无法保留多级目录的相对路径结构——它默认把所有文件展平到 ZIP 根下,或强制带上顶层文件夹名。真要按原始层级打包(比如 C:cile.txt 压进 ZIP 后路径仍是 bcile.txt),必须用 ZipOutputStream 手动控制每个 ZipEntry 的 Name。
为什么 ZipFile.CreateFromDirectory 会丢掉中间目录
ZipFile.CreateFromDirectory 的第二个参数是源目录路径,它只认这个“根”,所有子项都以该根为基准计算相对路径。如果你传 "C:\project\src",那 src\Program.cs 就是最终路径;但如果你本意是保留 project\src\Program.cs,它就做不到。
- 它不接受“裁剪前缀长度”或“自定义基准”的参数
- 即使你把整个
C:\project作为源目录传入,ZIP 包里也会多出project\这一层——而你可能只想从src开始保留结构 - 遇到空目录时直接跳过,无法在 ZIP 中创建对应目录条目
用 SharpZipLib 写 ZipOutputStream 时怎么设对 ZipEntry.Name
关键不是取 FileInfo.FullName,而是算出相对于“逻辑基准目录”的路径字符串,并确保反斜杠统一为正斜杠(ZIP 规范要求)。
- 先确定你要裁掉的前缀长度:比如基准目录是
"D:\app\v2\",那TrimLength = @"D:pp2".Length - 对每个
FileInfo,用file.FullName.Substring(TrimLength)得到相对路径 - 调用
.Replace('\', '/'),否则 Windows 路径里的会导致 ZIP 解压失败或路径错乱 - 如果是目录,结尾必须加
/(如"lib/net6.0/"),否则解压工具可能忽略它
示例:new ZipEntry("src/Program.cs".Replace('', '/')) —— 不要漏掉 .Replace。
空目录怎么写进 ZIP 包
ZipOutputStream 不会自动为你创建空目录条目,必须显式添加一个类型为 IsDirectory = true 的 ZipEntry。
- 遍历
DirectoryInfo.GetDirectories()时,对每个子目录生成一个ZipEntry - 设置
entry.IsDirectory = true,且Name必须以/结尾 - 调用
zos.PutNextEntry(entry),但不用Write()写数据 - 顺序上建议先写目录条目,再写文件条目,避免某些解压器报错
漏掉这步,ZIP 包里就只有文件,没有空文件夹——即便原目录结构里有 obj/Debug/ 这种空目录。
大文件或大量小文件容易卡死或 OOM
SharpZipLib 默认用内存缓冲写入,如果一次性读完整个大文件(如 fs.Read(buffer, 0, buffer.Length)),很容易触发 OutOfMemoryException。
- 改用分块读写:定义固定大小缓冲区(如
byte[8192]),循环Read()+Write() - 别用
FileStream.Length分配超大数组,尤其对网络文件或重定向流不可靠 - 记得在
finally或using中关闭FileStream,否则句柄泄漏会拖慢后续压缩 - 调用
zos.SetLevel(6)而非9,高压缩比对 CPU 和内存压力更大,实际场景中 6 更均衡
最常被忽略的是缓冲区复用和流未释放——写完一个文件不关流,下一个文件就可能因句柄占用失败。











