必须先用image.DecodeConfig探测尺寸和格式,再按需解码;JPEG大图应设jpeg.DecodeOptions{Scale:4}降采样,缩放用draw.ApproxBiLinear而非CatmullRom,WebP编码优先选libvips而非纯Go实现。

别用 image.Resize——它不存在,Go 标准库没这玩意儿;也别一上来就全量解码 5000×4000 的 JPEG,单次请求吃掉上百 MB 内存不是并发问题,是路径设计错了。
先用 image.DecodeConfig 探尺寸,别急着 image.Decode
大图直接 image.Decode 解码,内存暴涨是必然的。JPEG 5000×4000 解码后 RGB 数据约 60MB,再加上缩放临时缓冲,轻松破百 MB。
- 用
io.LimitReader(r, 512)读前 512 字节,传给image.DecodeConfig拿宽高和格式,不加载像素数据 - 根据目标尺寸反推降采样率:
jpeg.DecodeOptions{Scale: 4}(支持 1/2、1/4、1/8)能直接跳过大量解码计算 - 若
DecodeConfig报image: unknown format,说明 magic bytes 不匹配——别怪用户传错,先检查是否漏注册解码器:jpeg.RegisterFormat()、png.RegisterFormat()、gif.RegisterFormat()必须显式调用
draw.ApproxBiLinear 足够快,draw.CatmullRom 别乱用
插值算法不是越“高级”越好。视觉差异在 200×200 缩略图上几乎不可辨,但 CPU 和内存开销翻倍是实打实的。
-
draw.ApproxBiLinear:速度比CatmullRom快 3 倍,内存友好,适合头像、商品图等常规缩略图 -
draw.CatmullRom要求源图已预乘 alpha(如*image.NRGBA),否则边缘发虚或色偏;仅建议用于 >800px 的高清预览图 - 别在循环里反复 new 插值器实例——它无状态,全局复用一个即可
WebP 编码卡死?绕开纯 Go 实现
golang.org/x/image/webp 的 encoder 是纯 Go 实现,Quality=80 就能让 CPU 拉满,延迟从 200ms 涨到 2s+。
立即学习“go语言免费学习笔记(深入)”;
- 优先用
github.com/h2non/bimg(基于 libvips),支持 WebP 多线程缩放,Alpine 镜像需装libvips-dev和build-base,否则 runtime panic 报undefined symbol: vips_colourspace - 若必须用纯 Go:
webp.Options.Quality控制在 60–75 区间,质量下降不明显,耗时降 60% - 加文件级缓存:
filepath.Join(cacheDir, fmt.Sprintf("%s_%dx%d.webp", hash, w, h)),避免重复编码
上传时 Content-Type 是假的,http.DetectContentType 才是真的
前端用 FormData.append('file', blob),旧版 Safari 可能不带 Content-Type 或传成 application/octet-stream,单靠 req.Header.Get("Content-Type") 判定必挂。
- 读 multipart body 前 512 字节,用
http.DetectContentType校验 magic bytes - 只接受
image/jpeg、image/png、image/webp,其余一律拒收 - 用
multipart.Reader流式读取,边读边校验,超 10MB 立即中断;禁用req.ParseMultipartForm(32 ,它会把整个 body 塞进内存
最容易被忽略的是:缩略图生成不是“先解码再缩放再编码”一条直线——每一步都有隐性约束:格式注册、alpha 处理、色彩空间转换、尺寸合法性校验、流控与缓存。漏掉任意一环,线上就是 OOM 或 400 空响应。


















