<p>压缩率计算公式为(1 - zip文件大小 / 原始文件总大小) × 100%,需在zip.Writer.Close()后获取最终大小,原始大小为各文件os.Stat().Size()之和,且仅zip.Deflate生效。</p>

zip归档压缩率怎么算:本质是比原始总大小和ZIP文件体积
压缩率不是zip包自己带的元数据,得你手动算。核心公式就一个:(1 - zip文件大小 / 原始文件总大小) * 100%。注意这里“原始文件总大小”指所有待打包文件的os.Stat().Size()之和,不是磁盘占用(比如稀疏文件、硬链接),也不是解压后可能膨胀的内容(如文本转UTF-8 BOM等)。
为什么直接用zip.Writer写完再os.Stat就行,但要注意时机
Go 的 archive/zip 是流式写入,zip.Writer.Close() 才真正 flush 并写入 central directory —— 也就是 ZIP 文件最终定型的时刻。如果在 Close() 前就去 os.Stat(),很可能读到的是不完整或空文件,导致压缩率算成 NaN 或负数。
- 必须等
zipWriter.Close()返回 nil 后,再对输出文件调用os.Stat() - 如果用
bytes.Buffer作为底层io.Writer,则直接取buf.Len()即可,无需os.Stat() - 别在 defer 里统计大小——defer 在函数返回时才执行,而你需要的是 Close 后立刻获取
zip.Store 和 zip.Deflate 对压缩率的影响非常直观
Go 默认用 zip.Store(即无压缩),这时候压缩率恒为 0%。只有显式设了 header.Method = zip.Deflate,才真正启用 DEFLATE 压缩。但要注意:
-
zip.Deflate是 Go 标准库唯一支持的压缩方法,没有zip.BestCompression这类常量 - 极小文件(如 Deflate,zlib 底层也可能自动退化回
Store,这是 zlib 行为,不是 bug - 压缩率高度依赖内容:纯文本、日志、JSON 通常能压到 30%~70%,而 PNG、JPEG、已压缩二进制基本压不动,甚至略涨
实际代码里怎么安全地拿到两个大小并计算
下面这段逻辑能覆盖大多数场景,重点是顺序和错误检查:
立即学习“go语言免费学习笔记(深入)”;
srcFiles := []string{"a.log", "config.json", "README.md"}
var totalSrcSize int64
for _, f := range srcFiles {
fi, err := os.Stat(f)
if err != nil { continue } // 或按需 panic
totalSrcSize += fi.Size()
}
<p>dstZip := "out.zip"
zipFile, _ := os.Create(dstZip)
defer zipFile.Close()</p><p>zw := zip.NewWriter(zipFile)
for <em>, f := range srcFiles {
fh, </em> := zip.FileInfoHeader(fi)
fh.Method = zip.Deflate // 关键:启用压缩
fw, <em> := zw.CreateHeader(fh)
src, </em> := os.Open(f)
io.Copy(fw, src)
src.Close()
}
zw.Close() // 必须先 close,才能准确获取 zip 大小</p><p>zipStat, _ := os.Stat(dstZip)
compressRate := float64(zipStat.Size()) / float64(totalSrcSize)
fmt.Printf("压缩率: %.1f%%\n", (1-compressRate)*100)
真正容易被忽略的是:如果你打包的是目录(而非单个文件),totalSrcSize 必须递归统计所有普通文件,跳过 symlink、dir、device node;否则分母失真,压缩率就完全不可信。


















