核心是用os.stat().size()比对原始与压缩后文件的字节数,而非像素尺寸;需确保格式一致、文件已落盘且元数据统一,再计算压缩后大小/原始大小的比值。

怎么用 Go 读取原始和压缩后图片的文件大小
核心是比对 os.Stat() 返回的 Size(),不是像素尺寸,而是磁盘占用字节数。很多人误以为要算宽高比,其实“压缩比例”在工程中默认指体积压缩率(即 压缩后大小 / 原始大小)。
注意:必须确保两次读取的是同一张图的原始文件和明确经过压缩处理后的文件(比如用 golang.org/x/image/jpeg 重编码保存),不能拿 PNG 原图和 JPEG 压缩版直接比——格式差异会主导结果,失去参考意义。
- 用
os.Stat("origin.jpg").Size()获取原始大小 - 用
os.Stat("compressed.jpg").Size()获取压缩后大小 - 避免用
io.Copy或流式写入后没Close()就调Stat(),此时文件可能未落盘,大小为 0 - Windows 下注意路径分隔符,建议统一用
filepath.Join()
如何用 image/jpeg 包控制压缩质量并获取真实体积变化
Go 标准库不提供“一键压缩”函数,必须手动解码再编码。关键点在于:jpeg.Encode() 的第三个参数是 *jpeg.Options,其中 Quality 字段(范围 1–100)直接影响体积,但不是线性关系。
示例逻辑:
dstFile, _ := os.Create("compressed.jpg")
defer dstFile.Close()
// img 是 decode 后的 *image.RGBA
jpeg.Encode(dstFile, img, &jpeg.Options{Quality: 75})
-
Quality=100不等于“无损”,Go 的 JPEG 实现仍会做 DCT 变换,体积通常比原图小 5%–15% - 低于
Quality=30时,体积下降变缓,但画质崩坏明显,不建议用于生产 - 务必在
Encode()后调dstFile.Close(),否则Stat()可能读到旧大小
为什么直接比较文件大小有时不准
常见干扰项:EXIF 元数据、JPEG 的扫描模式(progressive vs baseline)、颜色空间(YCbCr vs RGB)、是否带 ICC profile。同一张图用不同库压缩,即使质量设为相同,体积也可能差 10% 以上。
- 用
exiftool -s -ImageSize -FileSize origin.jpg可人工验证元数据占比 - Go 默认解码 JPEG 会丢弃 EXIF,再编码时不会自动写回,导致压缩后文件更小——这不是压缩算法功劳,是元数据丢失
- 如需保留 EXIF,得用第三方库(如
github.com/rwcarlsen/goexif/exif)手动提取再注入,但会增加体积 - 若对比目的是评估算法效果,建议先用
convert input.jpg -strip output.jpg统一清理元数据再测
一个安全计算压缩比例的最小可运行片段
不依赖外部工具,只用标准库 + golang.org/x/image/jpeg,重点规避常见陷阱:
func calcCompressionRatio(orig, comp string) (float64, error) {
origStat, err := os.Stat(orig)
if err != nil {
return 0, err
}
compStat, err := os.Stat(comp)
if err != nil {
return 0, err
}
if origStat.Size() == 0 || compStat.Size() == 0 {
return 0, errors.New("file size is zero")
}
return float64(compStat.Size()) / float64(origStat.Size()), nil
}
调用前确保:comp 文件已完整写入且关闭;两张图格式一致;原始图本身没被其他工具预压缩过(比如手机直出 JPEG 往往已用 92% 质量压过)。真实场景中,这个比值波动比想象中大,别把 0.82 和 0.79 当作显著差异。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











