From 5fbd010b715c461f9b1a8be8b2840a5141a2627c Mon Sep 17 00:00:00 2001 From: Victor_Jay Date: Sun, 31 May 2026 02:59:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=87=E7=AB=A0=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=20JPEG/PNG=20=E8=87=AA=E5=8A=A8=E8=BD=AC=20W?= =?UTF-8?q?ebP=20=E5=8E=8B=E7=BC=A9=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - JPEG/PNG 上传后自动转为 WebP quality 80 存储,保留原尺寸不 resize - 仅当 WebP 比原文件小时才采用,否则回退原格式 - GIF/WebP 保持不变直存 - 移除对 common.SaveUploadedFile 的依赖,改用 io.ReadAll + os.WriteFile --- internal/controller/post_controller.go | 50 ++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/internal/controller/post_controller.go b/internal/controller/post_controller.go index a7668bd..a4d2a50 100644 --- a/internal/controller/post_controller.go +++ b/internal/controller/post_controller.go @@ -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 { - common.Error(c, http.StatusInternalServerError, "保存图片失败") - return + // 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}) }