webp fix v2

This commit is contained in:
tamakyi
2026-10-16 09:39:30 +08:00
parent e4457e3da4
commit da7e76a7db
10 changed files with 903 additions and 60 deletions
+17 -15
View File
@@ -668,6 +668,11 @@ type uploadImageOptions struct {
var ErrUploadImageSizeTooLarge = errors.New("图片文件大小不能大于 5Mb")
// fitsWithinSide 判断图片宽高是否都已在 maxSide 的边长限制内。
func fitsWithinSide(cfg image.Config, maxSide int) bool {
return cfg.Width > 0 && cfg.Height > 0 && cfg.Width <= maxSide && cfg.Height <= maxSide
}
func uploadImage(ctx context.Context, opts uploadImageOptions) error {
imgFile := opts.Image
fileName := imgFile.Filename
@@ -696,17 +701,16 @@ func uploadImage(ctx context.Context, opts uploadImageOptions) error {
var contentType string
var uploadKey string
// 检测原始格式(解码前先校验像素总量,防止解压炸弹)
if err := imagecheck.CheckPixelLimit(bytes.NewReader(originalData)); err != nil {
if errors.Is(err, imagecheck.ErrImageTooLarge) {
return imagecheck.ErrImageTooLarge
}
// 检测原始格式(只解析文件头,不解像素):既拿到 format 也拿到宽高,
// 供像素总量校验与 WebP 透传判断共用。此前此处 image.Decode 完整解码一遍
// 只为取 format,转换时还要再解一遍,峰值内存翻倍。
cfg, format, err := image.DecodeConfig(bytes.NewReader(originalData))
if err != nil {
return errors.Wrap(err, "decode image config")
}
_, format, err := image.Decode(bytes.NewReader(originalData))
if err != nil {
return errors.Wrap(err, "decode image")
if cfg.Width > 0 && cfg.Height > 0 &&
int64(cfg.Width)*int64(cfg.Height) > imagecheck.MaxImagePixels {
return imagecheck.ErrImageTooLarge
}
now := time.Now()
@@ -728,19 +732,17 @@ func uploadImage(ctx context.Context, opts uploadImageOptions) error {
uploadKey = fmt.Sprintf("%d/%d/%d%s", now.Year(), now.Month(), now.UnixNano(), ".webp")
}
default:
// JPEG / PNG 等静态图片转换为 WebP。这里只取 DecodeConfig(不波纹解像素),
// 避免在像素量已经 CheckPixelLimit 校验过的超大图上再解码一遍。
cfg, _, cfgErr := image.DecodeConfig(bytes.NewReader(originalData))
// JPEG / PNG 等静态图片转换为 WebP。
// 已是 WebP 且尺寸合规 -> 原样上传:再编码会白丢一代有损画质。
if format == "webp" && cfgErr == nil &&
cfg.Width <= storage.GeneralEncodeSpec.MaxSide && cfg.Height <= storage.GeneralEncodeSpec.MaxSide {
if format == "webp" &&
fitsWithinSide(cfg, storage.GeneralEncodeSpec.MaxSide) {
uploadData = originalData
contentType = "image/webp"
uploadKey = fmt.Sprintf("%d/%d/%d%s", now.Year(), now.Month(), now.UnixNano(), fileExt)
break
}
webpData, err := storage.ConvertStaticImageToWebP(bytes.NewReader(originalData), storage.GeneralEncodeSpec)
webpData, err := storage.ConvertStaticImageToWebP(originalData, storage.GeneralEncodeSpec)
if err != nil || len(webpData) >= len(originalData) {
// 转换失败,或转换后并不比原图小(小图标/线框图/已优化 JPEG 上有损 WebP
// 可能反而更大)—— 一律沿用原图。