feat: 文章图片上传 JPEG/PNG 自动转 WebP 压缩存储

- JPEG/PNG 上传后自动转为 WebP quality 80 存储,保留原尺寸不 resize
- 仅当 WebP 比原文件小时才采用,否则回退原格式
- GIF/WebP 保持不变直存
- 移除对 common.SaveUploadedFile 的依赖,改用 io.ReadAll + os.WriteFile
This commit is contained in:
2026-05-31 02:59:35 +08:00
parent da7560b918
commit 5fbd010b71

View File

@ -1,8 +1,14 @@
package controller
import (
"bytes"
"errors"
"fmt"
"image"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"io"
"net/http"
"os"
"path/filepath"
@ -14,6 +20,7 @@ import (
"metazone.cc/metalab/internal/model"
"metazone.cc/metalab/internal/service"
"github.com/chai2010/webp"
"github.com/gin-gonic/gin"
)
@ -358,6 +365,7 @@ var allowedImageExt = map[string]bool{
}
// UploadImage 上传帖子图片需登录multipart/form-data
// JPEG/PNG 自动转 WebP quality 80 存储,保留原尺寸不 resize
func (ctrl *PostController) UploadImage(c *gin.Context) {
uid, _, ok := common.GetGinUser(c)
if !ok {
@ -386,6 +394,13 @@ func (ctrl *PostController) UploadImage(c *gin.Context) {
return
}
// 读取全部数据(后续 WebP 转换需要)
data, err := io.ReadAll(file)
if err != nil {
common.Error(c, http.StatusInternalServerError, "读取文件失败")
return
}
// 存储路径
storageDir := "storage/uploads/posts"
if err := os.MkdirAll(storageDir, 0755); err != nil {
@ -393,17 +408,38 @@ func (ctrl *PostController) UploadImage(c *gin.Context) {
return
}
// 唯一文件名uid_timestamp.ext
ts := time.Now().UnixMilli()
filename := fmt.Sprintf("%d_%d%s", uid, ts, ext)
savePath := filepath.Join(storageDir, filename)
var savePath, url string
if err := common.SaveUploadedFile(file, savePath); err != nil {
// JPEG/PNG → WebP 压缩存储保留原尺寸quality 80
if ext == ".jpg" || ext == ".jpeg" || ext == ".png" {
img, _, err := image.Decode(bytes.NewReader(data))
if err == nil {
var buf bytes.Buffer
if err := webp.Encode(&buf, img, &webp.Options{Quality: 80}); err == nil {
// 仅当 WebP 比原文件小时才采用,否则回退原格式
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
}
}
}
}
}
// 回退GIF / WebP / 转换失败 / 转换后反而更大 → 原样存储
if savePath == "" {
filename := fmt.Sprintf("%d_%d%s", uid, ts, ext)
savePath = filepath.Join(storageDir, filename)
if err := os.WriteFile(savePath, data, 0644); err != nil {
common.Error(c, http.StatusInternalServerError, "保存图片失败")
return
}
url = "/uploads/posts/" + filename
}
url := "/uploads/posts/" + filename
common.VditorUploadOk(c, map[string]string{header.Filename: url})
}