Files
workspace/code/app/app-go/internal/handle/s3.go
T
2026-07-19 21:57:40 +08:00

165 lines
3.3 KiB
Go

package handle
import (
"allapp-go/internal/errors"
"allapp-go/internal/httpx"
"allapp-go/internal/types"
"allapp-go/pkg/logger"
"allapp-go/pkg/s3store"
"fmt"
"path/filepath"
"strconv"
"time"
"github.com/gofiber/fiber/v3"
"github.com/google/uuid"
"go.uber.org/zap"
)
func S3UploadFile(c fiber.Ctx) error {
form, err := c.MultipartForm()
if err != nil {
return errors.WithStack(err)
}
files := form.File["files"]
if len(files) == 0 {
return httpx.OK(c, nil)
}
now := time.Now()
folderPath, err := uploadFolderPath(c.FormValue("upload_type"), c.FormValue("family_id"), c.FormValue("upload_module"), now)
if err != nil {
return httpx.Fail(c, err.Error())
}
var result []string
for _, fileHeader := range files {
file, err := fileHeader.Open()
if err != nil {
return errors.WithStack(err)
}
ext := filepath.Ext(fileHeader.Filename)
newName := fmt.Sprintf("%s%s", uuid.NewString(), ext)
key := fmt.Sprintf("%s/%s", folderPath, newName)
contentType := fileHeader.Header.Get("Content-Type")
err = s3store.UploadToRustFS(
c.Context(),
file,
key,
fileHeader.Size,
contentType,
)
_ = file.Close()
if err != nil {
return errors.WithStack(err)
}
result = append(result, key)
}
return httpx.OK(c, result)
}
func S3CopyFile(c fiber.Ctx) error {
var req types.S3CopyReq
if err := httpx.BindAndValidate(c, &req); err != nil {
return errors.WithStack(err)
}
if len(req.Keys) == 0 {
return httpx.OK(c, []string{})
}
folderPath := familyDateFolder(req.Module, req.FamilyID, time.Now())
result := make([]string, 0, len(req.Keys))
for _, sourceKey := range req.Keys {
ext := filepath.Ext(sourceKey)
if ext == "" {
ext = ".bin"
}
targetKey := fmt.Sprintf("%s/%s%s", folderPath, uuid.NewString(), ext)
if err := s3store.CopyObject(c.Context(), sourceKey, targetKey); err != nil {
return errors.WithStack(err)
}
result = append(result, targetKey)
}
return httpx.OK(c, result)
}
func S3DeleteFile(c fiber.Ctx) error {
var req types.S3DeleteReq
if err := httpx.BindAndValidate(c, &req); err != nil {
return errors.WithStack(err)
}
log := logger.FromCtx(c.Context())
if err := s3store.DeleteBatch(c.Context(), req.Keys); err != nil {
log.Warn("S3 批量删除失败",
zap.Int("count", len(req.Keys)),
zap.Error(err),
)
// ❗仍然返回成功(符合你的要求)
}
return httpx.OK(c, nil)
}
func parseFamilyID(value string) (int64, error) {
familyID, err := strconv.ParseInt(value, 10, 64)
if err != nil || familyID <= 0 {
return 0, fmt.Errorf("invalid family_id")
}
return familyID, nil
}
func familyDateFolder(module string, familyID int64, now time.Time) string {
return fmt.Sprintf("business/%d/%s/%d/%02d/%02d",
familyID,
module,
now.Year(),
now.Month(),
now.Day(),
)
}
func avatarDateFolder(now time.Time) string {
return fmt.Sprintf("avatar/%d/%02d/%02d",
now.Year(),
now.Month(),
now.Day(),
)
}
func uploadFolderPath(uploadType string, familyIDValue string, module string, now time.Time) (string, error) {
if uploadType == "avatar" {
return avatarDateFolder(now), nil
}
familyID, err := parseFamilyID(familyIDValue)
if err != nil {
return "", fmt.Errorf("family_id不能为空")
}
if module == "" {
return "", fmt.Errorf("module不能为空")
}
return familyDateFolder(module, familyID, now), nil
}