Go标准库压缩解压需注意:1.gzip仅支持单文件,必须显式调用Close()写入CRC/ISIZE,否则解压失败;2.zip打包需设header.Method=zip.Deflate、路径转正斜杠、防Zip Slip;3.tar.gz须严格嵌套顺序,先tar后gzip并正确关闭。

Go 标准库能直接做压缩解压,但直接裸用 archive/zip、compress/gzip 或 archive/tar 容易踩坑——不是体积没变小,就是解压时覆盖系统文件,或者生成的包在 Windows 上中文名乱码。
gzip 压缩单个文件必须调用 Close(),否则解压失败
gzip 不是“写完就完事”的流封装,它依赖尾部 10 字节校验信息(CRC + ISIZE)。漏掉 gzWriter.Close(),生成的 .gz 文件开头可能是 0x1f 0x8b,但结尾残缺,gunzip -t 直接报 unexpected end of file。
- 别只写
defer gzWriter.Close()就返回——如果中间 panic 或提前 return,defer 不执行 -
os.Create()后必须用gzip.NewWriter()包裹,不能直接io.Copy(dst, src) - 大文件建议用
io.CopyBuffer(dst, src, make([]byte, 64*1024))控制内存占用,避免一次性加载全文 - 压缩级别选
gzip.BestSpeed(日志)、gzip.DefaultCompression(通用),gzip.NoCompression仅适合需要快速套壳、不真压缩的场景
zip 打包多文件必须手动设 header.Method = zip.Deflate
Go 的 zip.Writer 默认用 zip.Store,也就是零压缩归档。你看到的 .zip 文件体积和源文件总和几乎一样,不是 bug,是默认行为。
- 每个文件写入前,必须显式设置
header.Method = zip.Deflate,否则不会调 zlib - 路径必须转正斜杠:
header.Name = filepath.ToSlash(relPath),Windows 下传"sub\file.txt"会导致解压失败或静默跳过 - 空目录要手动加尾部斜杠:
header.Name = "dir/",且header.FileInfo().IsDir() == true - 中文文件名需开启 UTF-8 标志:
header.Flags = 1(Go < 1.22)或header.SetUTF8(true)(Go ≥ 1.22)
解压 ZIP 时不做路径校验等于开放系统写入权限
archive/zip 对 f.Name 完全信任,不拦截 "../../../etc/passwd" 这类路径。你拼出的 filepath.Join(outputDir, f.Name) 可能直接落到根目录甚至系统关键路径。
立即学习“go语言免费学习笔记(深入)”;
- 先统一转斜杠:
cleanName := filepath.ToSlash(filepath.Clean(f.Name)) - 拒绝含
".."或以"/"开头的路径:strings.Contains(cleanName, "..") || strings.HasPrefix(cleanName, "/") - 最终目标路径必须落在输出目录内:
absDst := filepath.Join(outputDir, cleanName),再检查filepath.HasPrefix(filepath.ToSlash(absDst), filepath.ToSlash(absOutputDir)) - 目录条目(
f.IsDir() == true)也要处理,用os.MkdirAll(filepath.Dir(absDst), 0755)创建父级,再确保cleanName以"/"结尾
tar.gz 是两层操作,顺序和关闭必须严格
tar.gz 不是单一格式,而是 tar 归档 + gzip 压缩嵌套。错一层,比如先 tar 再 gzip,或解压时没用 gzip.NewReader() 包装,就会报 archive/tar: invalid tar header。
- 压缩时:先
os.Create()→ 再gzip.NewWriter()→ 再tar.NewWriter();写完所有文件后,按顺序调tw.Close()→gw.Close()→f.Close() - 解压时:先
os.Open()→ 再gzip.NewReader()→ 再tar.NewReader();tr.Next()返回的Header.Name必须用filepath.Clean()净化,再os.MkdirAll(filepath.Dir(name), 0755) - 跨平台注意:
Header.Name中反斜杠必须转"/",Header.Mode是 Unix 权限位(如0644),不是字符串 - 老旧环境(如 BusyBox)可能不认 PAX 格式,需显式设
tw.Format = tar.FormatGNU
真正难的不是写对一行 io.Copy,而是路径净化、关闭顺序、编码标志、压缩模式这些细节——它们不出错时无声无息,一出错就是静默覆盖、解压失败或跨平台乱码。


















