diff --git a/infrastructure/storage/oss_storage.go b/infrastructure/storage/oss_storage.go new file mode 100644 index 0000000..1e8ff2c --- /dev/null +++ b/infrastructure/storage/oss_storage.go @@ -0,0 +1,122 @@ +package storage + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/aliyun/aliyun-oss-go-sdk/oss" + "github.com/drama-generator/backend/pkg/config" +) + +// OssStorage 阿里云 OSS 存储实现 +type OssStorage struct { + client *oss.Client + bucket *oss.Bucket + bucketName string + customDomain string + endpoint string +} + +// NewOssStorage 创建 OSS 存储实例 +func NewOssStorage(cfg *config.OssConfig) (*OssStorage, error) { + if cfg.Endpoint == "" || cfg.AccessKeyID == "" || cfg.AccessKeySecret == "" || cfg.BucketName == "" { + return nil, fmt.Errorf("OSS configuration is incomplete") + } + + client, err := oss.New(cfg.Endpoint, cfg.AccessKeyID, cfg.AccessKeySecret) + if err != nil { + return nil, fmt.Errorf("failed to create OSS client: %w", err) + } + + bucket, err := client.Bucket(cfg.BucketName) + if err != nil { + return nil, fmt.Errorf("failed to get OSS bucket: %w", err) + } + + return &OssStorage{ + client: client, + bucket: bucket, + bucketName: cfg.BucketName, + customDomain: cfg.CustomDomain, + endpoint: cfg.Endpoint, + }, nil +} + +// Upload 上传文件到 OSS +func (s *OssStorage) Upload(localPath, category string) (string, error) { + // 读取本地文件 + file, err := os.Open(localPath) + if err != nil { + return "", fmt.Errorf("failed to open file: %w", err) + } + defer file.Close() + + // 生成 OSS 对象键 + ext := filepath.Ext(localPath) + timestamp := time.Now().Format("20060102_150405_000") + objectKey := fmt.Sprintf("%s/%s%s", category, timestamp, ext) + + // 上传到 OSS + err = s.bucket.PutObject(objectKey, file) + if err != nil { + return "", fmt.Errorf("failed to upload to OSS: %w", err) + } + + // 构建访问 URL + url := s.GetURL(objectKey) + return url, nil +} + +// UploadWithFilename 使用指定文件名上传文件到 OSS +func (s *OssStorage) UploadWithFilename(localPath, category, filename string) (string, error) { + file, err := os.Open(localPath) + if err != nil { + return "", fmt.Errorf("failed to open file: %w", err) + } + defer file.Close() + + objectKey := fmt.Sprintf("%s/%s", category, filename) + + err = s.bucket.PutObject(objectKey, file) + if err != nil { + return "", fmt.Errorf("failed to upload to OSS: %w", err) + } + + return s.GetURL(objectKey), nil +} + +// GetURL 获取 OSS 对象的访问 URL +func (s *OssStorage) GetURL(objectKey string) string { + if s.customDomain != "" { + // 使用自定义域名(CDN) + domain := strings.TrimSuffix(s.customDomain, "/") + if !strings.HasPrefix(domain, "http") { + domain = "https://" + domain + } + return fmt.Sprintf("%s/%s", domain, objectKey) + } + + // 使用默认 OSS 域名 + return fmt.Sprintf("https://%s.%s/%s", s.bucketName, s.endpoint, objectKey) +} + +// Delete 删除 OSS 对象 +func (s *OssStorage) Delete(objectKey string) error { + err := s.bucket.DeleteObject(objectKey) + if err != nil { + return fmt.Errorf("failed to delete OSS object: %w", err) + } + return nil +} + +// IsConfigured 检查 OSS 是否已配置 +func IsOssConfigured(cfg *config.OssConfig) bool { + return cfg != nil && + cfg.Endpoint != "" && + cfg.AccessKeyID != "" && + cfg.AccessKeySecret != "" && + cfg.BucketName != "" +}