zip.writer默认使用zip.store无压缩,必须显式设置header.method = zip.deflate才能启用deflate压缩;空或极小文件可能自动回退store,已压缩格式再压无效;解压需校验路径防zip slip,中文名须设flags=1或setutf8(true)。

zip.Writer 默认不压缩,必须手动设 header.Method = zip.Deflate
你生成的 .zip 文件体积没变小?不是代码写错了,是 Go 标准库默认用 zip.Store —— 纯归档、零压缩。它把每个文件原样塞进 ZIP,连 zlib 都不调用。
- 每个文件写入前,必须显式设置
header.Method = zip.Deflate;CreateHeader或FileInfoHeader不会自动切换压缩模式 -
zip.Deflate是 Go 唯一支持的压缩方法,不存在zip.BestCompression这类常量 - 空文件或小于几十字节的文件,底层 zlib 可能自动回退到
Store,这不是 bug,是合理优化 - 已压缩格式(
.jpg、.png、.mp4)再压基本无效,甚至略增体积;文本类(.go、.json、.log)才真正受益
解压时不做路径校验,../../../etc/passwd 会直接覆盖系统文件
archive/zip 对 f.Name 完全放行,不拦截、不警告、不归一化。攻击者只要在 ZIP 里塞一个名字为 ../../config.yaml 的条目,你用 filepath.Join(dst, f.Name) 一拼,就可能覆盖服务配置甚至系统关键文件 —— 这就是 Zip Slip 漏洞。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 先调
filepath.Clean(f.Name)归一化路径(./a/../b→b,../x→../x) - 检查
cleanName != f.Name或strings.HasPrefix(cleanName, "..")或strings.HasPrefix(cleanName, "/") - Windows 下还要统一转小写、把
"\"替换为"/",否则"..\..\etc\passwd"可能绕过校验 - 目标路径必须严格落在输出目录下:
strings.HasPrefix(filepath.ToSlash(dstPath), filepath.ToSlash(absDest)) - 遇到
f.IsDir()为true,要创建目录(os.MkdirAll(..., 0755)),且确保cleanName以"/"结尾
中文文件名乱码,header.Flags = 1 是关键开关
Go 默认按 UTF-8 写文件名,但 Windows 资源管理器默认用 GBK 解码 —— 两边不匹配,名字就变成 ???.txt。别试图用 golang.org/x/text/encoding/simplifiedchinese 转 GBK:Windows 能认,Linux/macOS 解压就乱码。
- 写 ZIP 时,给每个
header加一句header.Flags = 1,表示文件名是 UTF-8 编码 - Go 1.22+ 可用
header.SetUTF8(true)替代,更语义化 - 别漏掉
header.SetModTime(time.Now()),否则所有文件时间戳一样,看着就很假 - 路径必须转正斜杠:
filepath.ToSlash(relPath),比如"sub\file.txt"→"sub/file.txt"
大文件解压卡死或 OOM,是因为误把整个 ZIP 读进内存
常见错误是 data, _ := io.ReadAll(zipFile) 再传给 zip.NewReader(bytes.NewReader(data), ) —— 几百 MB 的 ZIP 直接吃光内存。其实 archive/zip 本身支持流式读取,问题出在用法上。
- 从磁盘解压:用
zip.OpenReader(path),它内部基于os.File和io.Seeker,按需读 - 解压时别信 “
io.Copy(dst, f.Open())一行搞定” —— 必须控制缓冲区:io.CopyBuffer(dst, src, make([]byte, 32*1024)) - 压缩时也一样:用
f, _ := os.Open(srcPath)+io.Copy(writer, f),不缓存原始内容 -
zw.Close()不可省略,它负责写 ZIP 中央目录区,缺了就是非法 ZIP 文件
f.Open() 返回的 io.ReadCloser 必须 .Close()、压缩时每个 header.Method 手动设 zip.Deflate。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










