用 archive/zip 压缩目录需手动遍历:跳过子目录,对每个文件用 filepath.rel() 计算相对路径,替换反斜杠为正斜杠后调用 w.create() 写入;空目录需手动创建带 "/" 结尾的 fileheader 并设置 modedir;务必调用 w.close() 写入中央目录。

用 archive/zip 遍历目录并写入 zip.Writer
Go 标准库不提供一键压缩整个目录的函数,必须手动遍历文件树,逐个调用 zip.Writer.Create() 创建条目、再把文件内容写进去。核心逻辑是:打开目标目录 → 递归读取所有文件(跳过目录本身)→ 对每个文件计算相对于根目录的路径(用于 zip 内部路径)→ 调用 w.Create() → 复制文件内容。
关键点在于路径处理:如果源目录是 /tmp/myapp,其中有个文件 /tmp/myapp/conf/config.yml,那么它在 zip 中的路径应为 conf/config.yml,不能带绝对路径或上级路径(否则解压可能越界)。建议用 filepath.Rel() 计算相对路径。
示例片段:
root := "/tmp/myapp"
f, _ := os.Create("out.zip")
defer f.Close()
w := zip.NewWriter(f)
defer w.Close()
filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil // 跳过目录,只处理文件
}
rel, _ := filepath.Rel(root, path)
fw, _ := w.Create(rel) // 注意:这里 rel 必须是正斜杠分隔(Windows 下需替换)
src, _ := os.Open(path)
io.Copy(fw, src)
src.Close()
return nil
})
w.Close() // 必须显式调用,否则 zip 结尾数据不完整
Windows 下路径分隔符问题:用 strings.ReplaceAll(rel, "\", "/")
zip 规范要求内部路径使用正斜杠 /,而 filepath.Rel() 在 Windows 上返回反斜杠 。如果不转换,某些解压工具(如 macOS Finder、Linux unzip)可能无法正确识别路径,导致文件全部解压到当前目录或报错。
解决方式简单直接:
- 在调用
w.Create()前对rel做一次替换:strings.ReplaceAll(rel, "\", "/") - 不要用
filepath.ToSlash()—— 它在非 Windows 平台会把已有/变成/(看似无害但冗余),且语义不如显式替换清晰 - 注意:
rel可能是"."(根目录自身),此时替换后仍是".",而w.Create(".")会失败;应跳过该情况
避免空目录丢失:需要手动创建 zip.FileHeader 并设置 ModeDir
标准 filepath.WalkDir 不会为纯空目录生成回调,所以默认逻辑下 zip 包里没有目录结构,只有文件。如果项目依赖目录层级(比如 templates/ 或 static/css/),解压后会变成扁平结构。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
要保留空目录,得改用 filepath.Walk(它会进入目录并回调),并在遇到目录时手动构造 *zip.FileHeader:
fh := &zip.FileHeader{
Name: strings.ReplaceAll(rel, "\", "/") + "/",
Method: zip.Store,
Flags: 0x800, // 表示 UTF-8 编码的文件名
}
fh.SetMode(0o755 | fs.ModeDir)
fw, _ := w.CreateHeader(fh)
注意三点:
-
Name必须以/结尾,否则会被当作文本文件 -
SetMode()是必须的,仅设fs.ModeDir不够,还要确保权限位合理(常见用0o755) -
Flags = 0x800推荐加上,否则中文目录名在部分解压器中可能乱码
大目录压缩时内存和性能注意事项
zip.Writer 默认不缓冲,每次 io.Copy 都直接写入底层 io.Writer(如文件),所以内存占用低。但频繁小文件写入会导致系统调用过多,影响速度。
优化建议:
- 对小文件(
)可先读入 <code>[]byte,再一次性fw.Write(),减少 syscall - 避免在循环中反复调用
os.Open()后不Close()—— 文件描述符泄漏比内存更易触发错误 - 如果目标是高压缩率,
zip.Writer不支持Deflate以外算法;如需zstd或lz4,得换第三方库(如github.com/klauspost/compress),但那就不是标准 zip 格式了
真正容易被忽略的是 w.Close() —— 它不仅刷新缓冲区,还写入 zip 中央目录。漏掉这句,生成的 zip 文件无法被大多数工具识别,但 file out.zip 可能仍显示为 “Zip archive data”,造成误判。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










