应边压缩边累加总原始大小与总压缩大小后求比值,而非逐个计算再平均;需显式调用gzipWriter.Close()确保文件完整,用int64累加防溢出,并发时用sync.WaitGroup和atomic或mutex保护,跳过已压缩文件避免统计失真。

直接统计批量文件的平均压缩比,不能先压缩再算比值再求均值——那样会把所有原始/压缩文件全 load 进内存,100 个 50MB 文件就可能 OOM。必须边压缩边累计原始大小与压缩后大小,最后用总和相除。
compress/gzip.NewWriter 必须 defer Close() 才能拿到真实压缩大小
很多人漏掉 gzipWriter.Close(),导致写入缓冲区未刷新、尾部校验未写入,os.Stat().Size() 拿到的是不完整或错误的压缩文件大小。
- 每次压缩完必须显式调用
gzipWriter.Close(),否则压缩流没结束,文件尺寸不可信 - 不要依赖
io.Copy返回值判断完成——它只管复制字节,不管 gzip 尾部 - 压缩后立即用
os.Stat(outputPath).Size()获取最终大小,不是写入中途的临时值
原始大小和压缩大小必须用 int64 累加,避免溢出
单个文件可能上百 MB,1000 个就是几百 GB 原始数据量;用 int 或 uint32 累加原始总大小,32 位系统上不到 5 个 1GB 文件就溢出。
- 声明两个
int64变量:totalOriginal := int64(0)、totalCompressed := int64(0) - 每次成功压缩后:
totalOriginal += originalSize、totalCompressed += compressedSize - 最后算平均压缩比:
float64(totalCompressed) / float64(totalOriginal)(注意除零检查)
并发压缩时需用 sync.WaitGroup + mutex 保护累加变量
goroutine 并发写同一个 int64 变量是未定义行为,哪怕只是 +=,也可能丢计数。别信“int64 赋值原子”——+= 不是原子操作。
- 用
sync.Mutex包裹累加逻辑:mu.Lock(); totalOriginal += size; mu.Unlock() - 或改用
sync/atomic:atomic.AddInt64(&totalOriginal, originalSize) -
sync.WaitGroup控制所有 goroutine 完成后再计算最终比值,别在 worker 里提前除
跳过无法压缩的文件类型可避免干扰统计
对已经压缩过的格式(如 .jpg、.png、.mp4)强行 gzip,压缩比常 > 0.99,甚至膨胀。这类文件应跳过,否则拉低平均值,失去统计意义。
- 按扩展名过滤:
strings.HasSuffix(strings.ToLower(name), ".jpg") - 或用
mimetype库读取 magic bytes 判断是否已压缩(如 JPEG SOI marker0xFFD8) - 记录被跳过的文件数和原因,方便后续分析数据构成
最易被忽略的一点:压缩比本身是非线性指标,平均压缩比 ≠ 平均「原始/压缩」,而应是「总压缩大小 / 总原始大小」。拿 10 个文件各压一次再求比值平均,和把它们拼成一个大文件再压,结果完全不同——前者掩盖了小文件压缩效率低、大文件压缩率高的真实分布。所以务必坚持总和比,而不是逐个比再平均。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











