Go 的 compress/gzip 仅支持单文件压缩,非 ZIP 替代品;压缩需显式调用 Close() 写入尾部数据,否则文件损坏;解压 ZIP/tar.gz 必须路径清洗防 Zip Slip。

Go 的 compress/gzip 只能压单个文件,不是 ZIP 替代品 —— 想打包目录?必须配 archive/tar 或 archive/zip。
gzip.Writer 必须显式调用 Close(),否则生成的 .gz 文件损坏
很多人写完 io.Copy(gzWriter, src) 就结束,结果用 gunzip -t 检查报 unexpected end of file。这是因为 gzip.Writer 内部有缓冲,且 CRC 和 ISIZE 尾部数据只在 Close() 时写入。
-
defer gzWriter.Close()是安全写法,但注意它在函数 return 后才执行 —— 若中间 panic 或提前 return,可能漏掉 - 别用
os.Open直接读 .gz 文件内容来“验证”,那只是二进制流;解压必须用gzip.NewReader() - 压缩级别可选:
gzip.NoCompression(快)、gzip.BestSpeed(适合日志)、gzip.DefaultCompression(通用)
解压 .gz 文件时,gzip.NewReader 之后必须检查 io.EOF
gzip.Reader 不会在读完所有数据后自动返回 io.EOF,而是可能再读一次才触发。不显式处理会导致循环卡死或漏数据。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 最稳妥是直接用
io.Copy(out, gr),它内部已正确处理io.EOF - 若需逐块读(如限速、校验),每次
Read()后必须检查n和err:当err == io.EOF或n == 0 && err == nil时停止 -
gr.Close()只释放资源,不反映数据是否读完 —— 别靠它判断流边界
压缩多个文件?别硬套 gzip,该上 archive/zip 或 tar.gz
compress/gzip 天然不支持多文件或路径结构。强行拼接多个文件进一个 .gz,解压出来就是一团乱码。
立即学习“go语言免费学习笔记(深入)”;
- 用
archive/zip:路径必须转正斜杠(filepath.ToSlash(relPath)),否则 macOS / Linux 解压工具可能识别失败 - 每个文件写入前,要设
header.Method = zip.Deflate,否则默认Store(不压缩) - 解压 ZIP 时务必做路径校验:
cleanPath := filepath.Clean(file.Name),再检查cleanPath != file.Name || strings.HasPrefix(cleanPath, ".."),防 Zip Slip - 解压
tar.gz必须嵌套:gzip.NewReader(f)→tar.NewReader();顺序反了(比如tar.NewReader(gzipFile))会解析乱码
真正容易被忽略的不是语法,而是两件事:一是 gzip.Writer.Close() 被跳过导致文件损坏,二是解压 ZIP / tar.gz 时没做路径清洗,让 ../../../etc/passwd 这类路径直接落地 —— 这些都不是“写不出来”的问题,是“看起来写对了,跑起来出事”的坑。

















