go标准库原生支持zip/gzip,但需手动处理路径规范化、中文编码、权限保留及内存优化;archive/zip适用于多文件归档,compress/gzip适合单文件压缩。

Go 标准库原生支持 zip 和 gzip,无需第三方包就能完成大多数压缩/解压任务;但直接操作容易出错——比如中文路径乱码、空目录丢失、文件权限丢失、内存溢出等。
用 archive/zip 压缩多个文件或目录
标准库不提供“一键压缩目录”函数,必须手动遍历文件树并写入 zip.Writer。关键点在于路径规范化和文件头设置:
- 所有文件路径必须用
/分隔(Windows 下需替换\),否则在 macOS/Linux 解压后可能变成不可见的隐藏目录 - 目录项需以
/结尾,并设置FileInfo.IsDir() == true,否则解压工具可能忽略空目录 - 调用
zip.FileHeader.SetMode()保留可执行权限(尤其对 shell 脚本重要) - 避免一次性读取大文件进内存:用
io.Copy()流式写入
示例片段:
fw, _ := zw.CreateHeader(&zip.FileHeader{
Name: "src/main.go",
Method: zip.Deflate,
})
io.Copy(fw, f) // f 是 *os.File
用 archive/zip 解压时处理中文路径和权限
Go 1.16+ 默认使用 UTF-8 解析 zip 中的文件名,但很多 Windows 工具(如旧版 WinRAR)仍用 GBK 写入,导致解压后文件名是乱码:
立即学习“go语言免费学习笔记(深入)”;
- 先尝试按 UTF-8 解码;若失败,再用
golang.org/x/text/encoding/unicode或第三方库(如github.com/mholt/archiver/v4)做编码探测 -
zip.FileHeader.Mode()返回的是 MS-DOS 属性位,不是 Unix 权限;需额外调用header.Extra解析 Unix 扩展字段(如果存在)才能还原0755等权限 - 解压目标路径必须提前
os.MkdirAll(filepath.Dir(dst), 0755),否则遇到深层嵌套路径会失败
什么时候该用 compress/gzip 而不是 archive/zip
gzip 是单文件压缩格式,zip 是归档+压缩格式;二者不能混用:
- 只压缩一个日志文件?用
gzip更轻量,且curl -H "Accept-Encoding: gzip"后端可直连解压 - 需要保留多个文件结构、注释、时间戳?必须用
zip -
gzip.Reader不支持随机 seek,无法跳过头部解析;而zip.Reader可通过r.File[0].Open()按需打开某个文件,适合大 ZIP 包中只读取特定 entry - 注意:
gzip文件没有内建文件名,解压后默认是原文件名(来自Header.Name字段,但常为空)
os.OpenFile 权限参数在压缩/解压中的实际影响
很多人以为传 0644 就能控制解压后文件权限,其实不然:
- Linux/macOS 下,新建文件权限受
umask影响,os.OpenFile("x", os.O_CREATE, 0644)实际可能是0600 - 解压时应显式调用
os.Chmod(dst, header.Mode().Perm())(对 zip)或根据源文件元数据设置 - Windows 忽略大部分权限位,但
0200(写权限)会影响是否显示为只读属性 - 容器环境(如 Docker)中,默认
umask常为0022,务必测试目标运行时行为
真正麻烦的不是 API 调用,而是跨平台路径处理、编码兼容性和权限继承——这些细节不会报错,但会在用户机器上静默失效。










