backend/internal/logic/fileStorage/upload_img_logic.go

122 lines
3.0 KiB
Go
Raw Normal View History

2025-11-12 06:50:35 +00:00
package fileStorage
import (
2025-11-12 07:38:56 +00:00
"backend/pkg/permission/domain/token"
2025-11-12 06:50:35 +00:00
"context"
"encoding/base64"
"fmt"
"strings"
"time"
"backend/internal/svc"
"backend/internal/types"
"github.com/google/uuid"
"github.com/zeromicro/go-zero/core/logx"
)
type UploadImgLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
2025-11-12 07:38:56 +00:00
// NewUploadImgLogic 上傳圖片檔案
2025-11-12 06:50:35 +00:00
func NewUploadImgLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadImgLogic {
return &UploadImgLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UploadImgLogic) UploadImg(req *types.UploadImgReq) (resp *types.UploadResp, err error) {
// 驗證 base64 格式
content := req.Content
if strings.HasPrefix(content, "data:image") {
// 移除 data URL 前綴 (例如: data:image/png;base64,)
parts := strings.SplitN(content, ",", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid base64 image format")
}
content = parts[1]
}
// 解碼 base64
imgData, err := base64.StdEncoding.DecodeString(content)
if err != nil {
return nil, fmt.Errorf("failed to decode base64: %w", err)
}
// 驗證文件大小10MB 限制)
const maxImgSize = 10 << 20 // 10MB
if len(imgData) > maxImgSize {
return nil, fmt.Errorf("image size exceeds 10MB limit")
}
// 驗證圖片格式(檢查 magic bytes
mimeType, err := l.detectImageType(imgData)
if err != nil {
return nil, fmt.Errorf("invalid image format: %w", err)
}
// 生成唯一文件名
fileExt := l.getExtensionFromMimeType(mimeType)
fileName := fmt.Sprintf("%s%s", uuid.New().String(), fileExt)
2025-11-12 07:38:56 +00:00
objectPath := fmt.Sprintf("images/%s/%d/%s", token.UID(l.ctx), time.Now().Year(), fileName)
2025-11-12 06:50:35 +00:00
// 上傳到 S3
fileStorageUC := l.svcCtx.FileStorageUC
if err := fileStorageUC.UploadFromData(l.ctx, imgData, objectPath, mimeType); err != nil {
return nil, fmt.Errorf("failed to upload image: %w", err)
}
// 獲取公開 URL
fileUrl := fileStorageUC.GetPublicURL(l.ctx, objectPath)
return &types.UploadResp{
FileUrl: fileUrl,
FileSize: int64(len(imgData)),
MimeType: mimeType,
}, nil
}
// detectImageType 檢測圖片類型
func (l *UploadImgLogic) detectImageType(data []byte) (string, error) {
if len(data) < 4 {
return "", fmt.Errorf("file too small")
}
// 檢查常見圖片格式的 magic bytes
if len(data) >= 2 && data[0] == 0xFF && data[1] == 0xD8 {
return "image/jpeg", nil
}
if len(data) >= 8 && string(data[0:8]) == "\x89PNG\r\n\x1a\n" {
return "image/png", nil
}
if len(data) >= 6 && (string(data[0:6]) == "GIF87a" || string(data[0:6]) == "GIF89a") {
return "image/gif", nil
}
if len(data) >= 12 && string(data[8:12]) == "WEBP" {
return "image/webp", nil
}
return "", fmt.Errorf("unsupported image format")
}
// getExtensionFromMimeType 根據 MIME 類型獲取文件擴展名
func (l *UploadImgLogic) getExtensionFromMimeType(mimeType string) string {
switch mimeType {
case "image/jpeg":
return ".jpg"
case "image/png":
return ".png"
case "image/gif":
return ".gif"
case "image/webp":
return ".webp"
default:
return ".jpg"
}
}