backend/internal/logic/fileStorage/upload_img_logic.go

122 lines
3.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package fileStorage
import (
"backend/pkg/permission/domain/token"
"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
}
// NewUploadImgLogic 上傳圖片檔案
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)
objectPath := fmt.Sprintf("images/%s/%d/%s", token.UID(l.ctx), time.Now().Year(), fileName)
// 上傳到 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"
}
}