压缩比需手动计算为原始字节长度除以压缩后长度,即 float64(len([]byte(s))) / float64(buf.len()),且必须调用 w.close() 后读取 buf.len() 才准确。

用 brotli.Writer 压缩后对比原始字节长度
压缩比不是内置函数返回的值,得自己算:原始长度 ÷ 压缩后长度。Go 没有 brotli.CompressRatio() 这种封装,必须手动读取输入和输出字节数。
关键点是:不能直接对字符串调用 len(s) 当作原始大小——要按 UTF-8 编码后的字节长度算;压缩结果也必须等 Writer.Close() 后才能从底层 bytes.Buffer 或 io.ReadSeeker 中准确获取长度。
-
len([]byte(s))才是真实原始字节数,len(s)是 Unicode 码点数(对中文会严重低估) - 必须调用
w.Close(),否则buf.Len()返回的是未 flush 的缓冲区大小,通常偏小甚至为 0 - 别用
io.Copy(w, strings.NewReader(s))后直接查buf.Len()—— 这跳过了 close 步骤,结果不可靠
完整可运行的计算示例
下面这段代码能稳定复现压缩比计算逻辑,适用于调试或基准测试:
package main
import (
"bytes"
"fmt"
"io"
"strings"
"github.com/andybalholm/brotli"
)
func main() {
s := "Hello, 世界! ".repeat(10000) // 构造约 150KB 的测试文本
src := []byte(s)
var buf bytes.Buffer
w := brotli.NewWriter(&buf)
w.Write(src)
w.Close() // 必须!
ratio := float64(len(src)) / float64(buf.Len())
fmt.Printf("原始: %d B → 压缩: %d B → 压缩比: %.2f\n", len(src), buf.Len(), ratio)
}
注意:strings.Repeat 不是标准库函数,实际要用 strings.Repeat(s, n);示例中省略了 import 和 error 检查,但生产环境必须检查 w.Write() 和 w.Close() 的返回值。
brotli.Writer 的 Quality 参数显著影响结果
同一段内容,不同 Quality 设置下压缩比差异可达 30% 以上。它不是“越高越好”,而是需要权衡 CPU 开销与收益。
-
Quality: 0是最快模式(类似gzip.BestSpeed),压缩比最低,但耗时最短 -
Quality: 6是推荐平衡点,适合 ≥1MB 的 HTML/JSON -
Quality: 11压缩比最高,但 CPU 占用翻倍,且首次调用有初始化抖动 - 不要在高频 API 中设 >6,实测 200KB 字符串下,Quality=11 比 =6 多花 3.2ms,压缩比仅提升 1.7%
线上服务中容易忽略的干扰项
你本地跑出来的压缩比,上线后可能完全对不上。常见干扰来自 HTTP 层和中间件:
- Nginx 默认不处理
br编码响应,除非显式配置brotli on;和匹配的brotli_types(比如漏掉application/json) - 如果 handler 中提前写了部分响应(如多次
w.Write()+Flush()),brotli.Writer可能被截断,导致压缩不完整、长度失真 - CDN 或反向代理若没透传
Vary: Accept-Encoding,可能缓存了明文版本,你以为压了,其实没生效 - 浏览器开发者工具的 Network 面板有时显示“from memory cache”,掩盖了真实编码,验证务必用
curl -H "Accept-Encoding: br" -I URL
压缩比数字本身没意义,真正重要的是:它是否在目标环境中被端到端正确应用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











