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