feat: align storage object keys

This commit is contained in:
2026-01-08 15:22:40 +08:00
parent 675e7a6783
commit 6ffe58ffb4

View File

@@ -8,9 +8,11 @@ import (
"io"
"mime/multipart"
"os"
"path"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
"quyun/v2/app/errorx"
@@ -93,6 +95,16 @@ type UploadMeta struct {
MimeType string
}
func (s *common) buildObjectKey(tenant *models.Tenant, hash, filename string) string {
// 按租户维度组织对象路径quyun/<tenant_uuid>/<hash>.<ext>
tenantUUID := "public"
if tenant != nil && tenant.UUID.String() != "" {
tenantUUID = tenant.UUID.String()
}
ext := strings.ToLower(filepath.Ext(filename))
return path.Join("quyun", tenantUUID, hash+ext)
}
func (s *common) InitUpload(ctx context.Context, userID int64, form *common_dto.UploadInitForm) (*common_dto.UploadInitResponse, error) {
uploadID := uuid.NewString()
localPath := s.storage.Config.LocalPath
@@ -177,9 +189,8 @@ func (s *common) CompleteUpload(ctx context.Context, userID int64, form *common_
}
sort.Ints(parts)
objectKey := uuid.NewString() + "_" + meta.Filename
dstPath := filepath.Join(localPath, objectKey)
dst, err := os.Create(dstPath)
mergedPath := filepath.Join(tempDir, "merged")
dst, err := os.Create(mergedPath)
if err != nil {
return nil, errorx.ErrInternalError.WithCause(err)
}
@@ -205,7 +216,6 @@ func (s *common) CompleteUpload(ctx context.Context, userID int64, form *common_
hash := hex.EncodeToString(hasher.Sum(nil))
dst.Close() // Ensure flush before potential removal
os.RemoveAll(tempDir)
// Deduplication Logic (Similar to Upload)
t, err := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.UserID.Eq(userID)).First()
@@ -214,15 +224,17 @@ func (s *common) CompleteUpload(ctx context.Context, userID int64, form *common_
tid = t.ID
}
objectKey := s.buildObjectKey(t, hash, meta.Filename)
existing, err := models.MediaAssetQuery.WithContext(ctx).Where(models.MediaAssetQuery.Hash.Eq(hash)).First()
var asset *models.MediaAsset
if err == nil {
os.Remove(dstPath) // Delete duplicate
os.Remove(mergedPath) // Delete duplicate
myExisting, err := models.MediaAssetQuery.WithContext(ctx).
Where(models.MediaAssetQuery.Hash.Eq(hash), models.MediaAssetQuery.UserID.Eq(userID)).
First()
if err == nil {
os.RemoveAll(tempDir)
return s.composeUploadResult(myExisting), nil
}
asset = &models.MediaAsset{
@@ -237,6 +249,13 @@ func (s *common) CompleteUpload(ctx context.Context, userID int64, form *common_
Meta: existing.Meta,
}
} else {
finalPath := filepath.Join(localPath, objectKey)
if err := os.MkdirAll(filepath.Dir(finalPath), 0o755); err != nil {
return nil, errorx.ErrInternalError.WithCause(err)
}
if err := os.Rename(mergedPath, finalPath); err != nil {
return nil, errorx.ErrInternalError.WithCause(err)
}
asset = &models.MediaAsset{
TenantID: tid,
UserID: userID,
@@ -253,6 +272,7 @@ func (s *common) CompleteUpload(ctx context.Context, userID int64, form *common_
}
}
os.RemoveAll(tempDir)
if err := models.MediaAssetQuery.WithContext(ctx).Create(asset); err != nil {
return nil, errorx.ErrDatabaseError.WithCause(err)
}
@@ -303,8 +323,6 @@ func (s *common) Upload(
) (*common_dto.UploadResult, error) {
// But this Upload endpoint accepts file. So we save it.
objectKey := uuid.NewString() + "_" + file.Filename
// Save file content to local storage
src, err := file.Open()
if err != nil {
@@ -316,13 +334,13 @@ func (s *common) Upload(
if localPath == "" {
localPath = "./storage" // Fallback
}
dstPath := filepath.Join(localPath, objectKey)
if err := os.MkdirAll(filepath.Dir(dstPath), 0o755); err != nil {
tmpDir := filepath.Join(localPath, "temp", "uploads", uuid.NewString())
if err := os.MkdirAll(tmpDir, 0o755); err != nil {
return nil, errorx.ErrInternalError.WithCause(err).WithMsg("failed to create storage directory")
}
tmpPath := filepath.Join(tmpDir, "file")
dst, err := os.Create(dstPath)
dst, err := os.Create(tmpPath)
if err != nil {
return nil, errorx.ErrInternalError.WithCause(err).WithMsg("failed to create destination file")
}
@@ -343,13 +361,15 @@ func (s *common) Upload(
tid = t.ID
}
objectKey := s.buildObjectKey(t, hash, file.Filename)
var asset *models.MediaAsset
// Deduplication Check
existing, err := models.MediaAssetQuery.WithContext(ctx).Where(models.MediaAssetQuery.Hash.Eq(hash)).First()
if err == nil {
// Found existing file (Storage Deduplication)
os.Remove(dstPath) // Delete the duplicate we just wrote
os.Remove(tmpPath) // Delete the duplicate we just wrote
os.RemoveAll(tmpDir)
// Check if user already has it (Logic Deduplication)
myExisting, err := models.MediaAssetQuery.WithContext(ctx).
@@ -372,6 +392,15 @@ func (s *common) Upload(
Meta: existing.Meta,
}
} else {
dstPath := filepath.Join(localPath, objectKey)
if err := os.MkdirAll(filepath.Dir(dstPath), 0o755); err != nil {
return nil, errorx.ErrInternalError.WithCause(err).WithMsg("failed to create storage directory")
}
if err := os.Rename(tmpPath, dstPath); err != nil {
return nil, errorx.ErrInternalError.WithCause(err).WithMsg("failed to finalize file")
}
os.RemoveAll(tmpDir)
// New unique file
asset = &models.MediaAsset{
TenantID: tid,