用 archive/zip 计算 zip 文件压缩比需遍历 zip.file 累加 uncompressedsize64 和 compressedsize64,不可用 os.stat(zippath).size() 直接计算;小文件场景因 zip 元数据开销会导致压缩比失真,且需注意编码兼容性与未压缩文件(method == zip.store)的处理。

用 archive/zip 读取原始文件大小和压缩后大小
Go 标准库不直接提供“压缩比”计算函数,必须手动对比原始字节长度与 ZIP 包内每个文件的 UncompressedSize64 和 CompressedSize64。关键点在于:ZIP 文件头里记录的是单个文件的压缩前后大小,不是整个 ZIP 包的总压缩率。
常见错误是直接用 os.Stat(zipPath).Size() 除以所有源文件 os.Stat(src).Size() 总和——这忽略了 ZIP 元数据开销(目录项、CRC、额外字段等),结果偏高(比如算出 95% 压缩率,实际只有 87%)。
正确做法是打开 ZIP 文件,遍历 *zip.File,累加 File.UncompressedSize64 和 File.CompressedSize64:
zr, _ := zip.OpenReader("out.zip")
defer zr.Close()
var totalUncompressed, totalCompressed int64
for _, f := range zr.File {
totalUncompressed += int64(f.UncompressedSize64)
totalCompressed += int64(f.CompressedSize64)
}
ratio := float64(totalCompressed) / float64(totalUncompressed)
批量处理时注意文件路径和编码兼容性
Windows 下 ZIP 文件常用 GBK 或 CP437 编码存文件名,而 Go 的 archive/zip 默认按 UTF-8 解析。若源文件名含中文且 ZIP 是用 WinRAR/7-Zip 默认方式生成的,f.Name 可能乱码,但 f.UncompressedSize64 不受影响——所以压缩比计算本身不受影响,但后续做文件名匹配(比如校验是否漏压)会失败。
解决路径匹配问题的实操建议:
- 若只需压缩比,跳过文件名解析,只依赖
UncompressedSize64和CompressedSize64 - 若需关联原始路径,统一用
filepath.Base(f.Name)提取文件名,并在压缩前确保源文件名不含非法字符(如/、\0) - 避免用
f.Name做os.Stat查原始大小——应提前保存原始os.FileInfo.Size()到 map 中,以索引或哈希为 key
为什么不能用 gzip 或 compress/zlib 替代 archive/zip
compress/gzip 和 compress/zlib 是流式压缩算法包,它们处理的是单个数据流,不包含文件元信息。ZIP 是归档格式,一个 ZIP 文件可含多个文件+目录结构+时间戳+权限位,而 GZIP 文件只能对应一个原始文件(哪怕你把多个文件先 tar 再 gzip,那也是 TAR+GZIP 组合,不是纯 GZIP)。
因此:
- 用
gzip.Reader打开 ZIP 文件会报gzip: invalid header - 想算“把 10 个 .log 文件打包成 1 个 .tar.gz 后的压缩比”,得先用
archive/tar构建 tar 流,再套gzip.Writer,最后分别统计 tar 流原始大小(即所有文件 size 总和 + tar header 开销)和 gzip 输出大小 - 标准 ZIP 压缩比计算,坚持用
archive/zip,别绕路
小文件多时压缩比失真的真实原因
当批量压缩上百个几 KB 的文本文件时,计算出的压缩比可能异常低(比如 120%,即压缩后反而更大)。这不是代码 bug,而是 ZIP 固有开销所致:每个文件至少占用 30 字节左右的本地文件头 + 46 字节中心目录项,外加对齐填充。如果单个文件平均小于 1KB,元数据占比可能超 20%。
这时需要区分两种“压缩比”:
- 内容压缩比:仅看 deflate 算法对原始内容的压缩效果(需解压后比对,不实用)
- 落地 ZIP 文件压缩比:用户真正关心的——磁盘上节省了多少空间,必须包含 ZIP 头部开销
所以遇到小文件场景,不要怀疑计算逻辑,要接受这个结果:ZIP 本就不适合极小文件集合。可考虑先 tar 合并再 gzip,或改用 zstd + 自定义索引格式。
真正容易被忽略的是:ZIP 中某些文件可能未压缩(f.Method == zip.Store),比如已压缩的 .jpg 或 .png,它们的 CompressedSize64 == UncompressedSize64。计算前最好过滤掉这类文件,或单独标记说明。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











