zip.writer 不能直接打包文件夹,需用 filepath.walkdir 遍历并手动创建 header;须用 filepath.toslash 规范路径、设置 header.modtime 保留修改时间、header.flags |= 0x800 支持 utf-8 文件名、流式写入避免 oom。

zip.Writer 不能直接写文件夹,必须手动遍历
Go 的 archive/zip 没有“一键打包目录”函数,zip.Writer 只认单个 io.Writer 流,不理解路径层级。你传一个文件夹路径进去,它不会自动递归——会直接报错或生成空 zip。
正确做法是用 filepath.WalkDir(Go 1.16+)或 filepath.Walk 遍历,对每个文件调用 zipWriter.CreateHeader 构造带路径的 zip.FileHeader,再把文件内容写进去。
- 务必用
filepath.ToSlash转换路径分隔符,Windows 下的\会导致 zip 内部路径损坏,解压失败 - 空文件夹需要显式创建:对目录项调用
zipWriter.CreateHeader,并确保FileHeader.IsDir()为 true,且 header.Name 以/结尾 - 不要用
os.Open后直接io.Copy到zip.FileWriter——要先调用CreateHeader获取 writer,再写内容
文件时间戳丢失?默认用 Unix 零值,不是源文件 mtime
如果不手动设置,zip.FileHeader.ModTime 默认是 time.Unix(0, 0),所有文件在解压后显示为 1970 年。这不是 bug,是 Go zip 包的默认行为。
修复很简单:遍历到每个文件时,用 os.Stat 拿到 FileInfo,取 ModTime() 赋给 header.ModTime。
立即学习“go语言免费学习笔记(深入)”;
-
ModTime只支持秒级精度,纳秒会被截断,和大多数 zip 工具一致 - 别用
time.Now()替代——用户要的是原始修改时间,不是打包时刻 - 注意:如果源文件是符号链接,
os.Stat返回的是链接本身的时间,os.Lstat才能区分
中文路径乱码?Zip 本身不支持 UTF-8,得开通用位标志
默认 zip 格式把文件名当 CP437 编码处理,中文路径写进去,WinRAR 或 macOS 归档工具会显示乱码或报错。这不是 Go 的问题,是 zip 规范的老坑。
解决方案是设置 FileHeader.Flags 的第 11 位(bit 11),即 0x800,告诉解压器“这名字是 UTF-8”。Go 从 1.16 开始支持这个标记,但不会自动设。
- 必须在调用
zipWriter.CreateHeader前设置:header.Flags |= 0x800 - 只对文件名(
header.Name)生效,注释、extra 字段等不在此列 - 老版本解压器(如 Windows 自带的“压缩文件夹”)可能忽略该标志,建议加兼容性提示
内存暴涨或 OOM?大文件别全读进 []byte
常见错误是用 os.ReadFile 把几百 MB 的文件一次性加载进内存,再 io.Copy 进 zip,结果程序吃光 RAM。
正确方式是打开文件后,用 io.Copy 直接流式写入 *zip.Writer 返回的 io.Writer,全程无缓冲区放大。
- 避免
bytes.Buffer或strings.Builder中转——它们本质还是内存拷贝 - 如果需加密或校验,可在写入前套一层
hash.Hash或crypto/cipher.Stream,仍保持流式 - 注意:调用
zipWriter.Close()前必须确保所有文件写完,否则 zip 结尾结构损坏,无法解压
路径处理、时间戳、编码、流控——这四点漏掉任一个,打出来的 zip 就可能在某个环境里打不开。尤其是 0x800 标志和 filepath.ToSlash,看着小,实际是跨平台可用性的分水岭。










