package logic import ( "context" "fmt" "path/filepath" "strings" "time" "git.cialloo.com/CiallooWeb/Blog/app/internal/svc" "git.cialloo.com/CiallooWeb/Blog/app/internal/types" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/zeromicro/go-zero/core/logx" ) type UploadPresignedURLLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } // Get presigned URL for file upload func NewUploadPresignedURLLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadPresignedURLLogic { return &UploadPresignedURLLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } // getFileType categorizes file extensions into type folders func getFileType(ext string) string { ext = strings.ToLower(ext) imageExts := map[string]bool{ ".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".bmp": true, ".webp": true, ".svg": true, ".ico": true, ".tiff": true, ".tif": true, } videoExts := map[string]bool{ ".mp4": true, ".avi": true, ".mov": true, ".wmv": true, ".flv": true, ".mkv": true, ".webm": true, ".m4v": true, ".mpg": true, ".mpeg": true, } audioExts := map[string]bool{ ".mp3": true, ".wav": true, ".flac": true, ".aac": true, ".ogg": true, ".wma": true, ".m4a": true, ".opus": true, ".ape": true, } archiveExts := map[string]bool{ ".zip": true, ".rar": true, ".7z": true, ".tar": true, ".gz": true, ".bz2": true, ".xz": true, ".iso": true, } documentExts := map[string]bool{ ".pdf": true, ".doc": true, ".docx": true, ".xls": true, ".xlsx": true, ".ppt": true, ".pptx": true, ".txt": true, ".rtf": true, ".odt": true, ".ods": true, ".odp": true, } switch { case imageExts[ext]: return "images" case videoExts[ext]: return "videos" case audioExts[ext]: return "audios" case archiveExts[ext]: return "archives" case documentExts[ext]: return "documents" default: return "others" } } func (l *UploadPresignedURLLogic) UploadPresignedURL(req *types.UploadPresignedURLReq) (resp *types.UploadPresignedURLResp, err error) { // Generate unique file key ext := filepath.Ext(req.FileName) // Determine file type based on extension fileType := getFileType(ext) // Generate file key grouped by type: {type}/{time}{ext} fileKey := fmt.Sprintf("%s/%d%s", fileType, time.Now().UnixMilli(), ext) // Calculate expiration time expiration := time.Duration(l.svcCtx.Config.S3.PresignedURLExpiration) * time.Second expireAt := time.Now().Add(expiration) // Create presigned client presignClient := s3.NewPresignClient(l.svcCtx.S3Client) // Generate presigned PUT URL putObjectInput := &s3.PutObjectInput{ Bucket: &l.svcCtx.Config.S3.Bucket, Key: &fileKey, } presignedReq, err := presignClient.PresignPutObject(l.ctx, putObjectInput, func(opts *s3.PresignOptions) { opts.Expires = expiration }) if err != nil { l.Errorf("Failed to generate presigned URL: %v", err) return nil, err } // Insert record into tmpfiles table query := `INSERT INTO tmpfiles (file_name, key, presigned_url, expiration) VALUES ($1, $2, $3, $4)` _, err = l.svcCtx.DB.ExecContext(l.ctx, query, req.FileName, fileKey, presignedReq.URL, expireAt) if err != nil { l.Errorf("Failed to insert tmpfile record: %v", err) return nil, err } return &types.UploadPresignedURLResp{ Url: presignedReq.URL, FileKey: fileKey, ExpireAt: expireAt.Unix(), }, nil }