perf: 文章图片 WebP 转换改为二分查找质量,目标 ≤ 原文件大小
- 不再仅试 quality 80,改为二分查找 (50~80) 找最高质量 ≤ 原文件大小 - 新增 encodeWebPBinary 辅助函数:先试 80,太大则二分降到 50 - 降到 50 仍超限时回退原格式,避免产生马赛克级质量 - 与头像的激进二分(最低 quality 1)区分,文章图片保底 quality 50
This commit is contained in:
@ -416,22 +416,19 @@ func (ctrl *PostController) UploadImage(c *gin.Context) {
|
||||
isGIF := ext == ".gif"
|
||||
|
||||
// 强制解码验证文件是否为有效图片(防伪扩展名、图片投毒),验证失败直接拒绝
|
||||
// JPEG/PNG:解码后顺便做 WebP 压缩
|
||||
// JPEG/PNG:解码后做 WebP 压缩(二分查找质量,目标 ≤ 原文件大小)
|
||||
if isJPEGPNG {
|
||||
img, _, err := image.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
common.Error(c, http.StatusBadRequest, "图片格式无效,请上传有效的图片文件")
|
||||
return
|
||||
}
|
||||
// WebP 压缩存储(保留原尺寸,quality 80)
|
||||
var buf bytes.Buffer
|
||||
if err := webp.Encode(&buf, img, &webp.Options{Quality: 80}); err == nil {
|
||||
if buf.Len() > 0 && buf.Len() < len(data) {
|
||||
filename := fmt.Sprintf("%d_%d.webp", uid, ts)
|
||||
savePath = filepath.Join(storageDir, filename)
|
||||
if err := os.WriteFile(savePath, buf.Bytes(), 0644); err == nil {
|
||||
url = "/uploads/posts/" + filename
|
||||
}
|
||||
webpData := encodeWebPBinary(img, len(data))
|
||||
if webpData != nil && len(webpData) < len(data) {
|
||||
filename := fmt.Sprintf("%d_%d.webp", uid, ts)
|
||||
savePath = filepath.Join(storageDir, filename)
|
||||
if err := os.WriteFile(savePath, webpData, 0644); err == nil {
|
||||
url = "/uploads/posts/" + filename
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -452,7 +449,7 @@ func (ctrl *PostController) UploadImage(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// 原格式存储(WebP / GIF / 转换后反而更大)
|
||||
// 原格式存储(GIF / 已有 WebP / 二分压缩仍超原文件大小)
|
||||
if savePath == "" {
|
||||
filename := fmt.Sprintf("%d_%d%s", uid, ts, ext)
|
||||
savePath = filepath.Join(storageDir, filename)
|
||||
@ -474,3 +471,33 @@ func (ctrl *PostController) applyShortcode(post *model.Post) {
|
||||
}
|
||||
}
|
||||
|
||||
// encodeWebPBinary 二分查找 WebP 质量,找 ≤ targetBytes 的最高质量(quality 50~80)
|
||||
// 返回 nil 表示最低质量仍超限,应由调用方回退原格式
|
||||
func encodeWebPBinary(img image.Image, targetBytes int) []byte {
|
||||
var buf bytes.Buffer
|
||||
if err := webp.Encode(&buf, img, &webp.Options{Quality: 80}); err != nil {
|
||||
return nil
|
||||
}
|
||||
if buf.Len() <= targetBytes {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
low, high := 50, 80
|
||||
var best []byte
|
||||
for low <= high {
|
||||
mid := (low + high) / 2
|
||||
buf.Reset()
|
||||
if err := webp.Encode(&buf, img, &webp.Options{Quality: float32(mid)}); err != nil {
|
||||
return nil
|
||||
}
|
||||
if buf.Len() <= targetBytes {
|
||||
best = make([]byte, buf.Len())
|
||||
copy(best, buf.Bytes())
|
||||
low = mid + 1
|
||||
} else {
|
||||
high = mid - 1
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user