package util import ( "bytes" "fmt" "io" "net/http" "time" "github.com/aliyun/aliyun-oss-go-sdk/oss" "github.com/aliyun/credentials-go/credentials" "github.com/topfans/backend/pkg/logger" "go.uber.org/zap" ) // OSSConfig OSS 配置 type OSSConfig struct { Region string BucketName string RoleArn string AccessKeyID string AccessKeySecret string } // UploadImageToOSS 下载图片并上传到 OSS func UploadImageToOSS(config OSSConfig, imageURL string, ossKey string) (string, error) { // 1. 下载图片到内存 resp, err := http.Get(imageURL) if err != nil { return "", fmt.Errorf("下载图片失败: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("下载图片失败,状态码: %d", resp.StatusCode) } imageData, err := io.ReadAll(resp.Body) if err != nil { return "", fmt.Errorf("读取图片数据失败: %w", err) } // 2. 获取 STS 临时凭证 credConfig := new(credentials.Config). SetType("ram_role_arn"). SetAccessKeyId(config.AccessKeyID). SetAccessKeySecret(config.AccessKeySecret). SetRoleArn(config.RoleArn). SetRoleSessionName("topfans-asset-upload"). SetPolicy(""). SetRoleSessionExpiration(3600) provider, err := credentials.NewCredential(credConfig) if err != nil { return "", fmt.Errorf("创建凭证提供器失败: %w", err) } cred, err := provider.GetCredential() if err != nil { return "", fmt.Errorf("获取临时凭证失败: %w", err) } // 3. 创建 OSS 客户端 endpoint := fmt.Sprintf("https://oss-%s.aliyuncs.com", config.Region) client, err := oss.New(endpoint, *cred.AccessKeyId, *cred.AccessKeySecret, oss.SecurityToken(*cred.SecurityToken)) if err != nil { return "", fmt.Errorf("创建OSS客户端失败: %w", err) } // 4. 获取 Bucket bucket, err := client.Bucket(config.BucketName) if err != nil { return "", fmt.Errorf("获取Bucket失败: %w", err) } // 5. 上传图片 err = bucket.PutObject(ossKey, bytes.NewReader(imageData)) if err != nil { return "", fmt.Errorf("上传图片到OSS失败: %w", err) } // 6. 构建 OSS URL ossURL := fmt.Sprintf("https://%s.oss-%s.aliyuncs.com/%s", config.BucketName, config.Region, ossKey) logger.Logger.Info("图片上传到OSS成功", zap.String("oss_key", ossKey), zap.String("oss_url", ossURL), ) return ossURL, nil } // DeleteObjectFromOSS 删除 OSS 对象(用于阶段一取消后清理素材) func DeleteObjectFromOSS(config OSSConfig, ossKey string) error { if ossKey == "" { return fmt.Errorf("ossKey不能为空") } // 获取 STS 临时凭证 credConfig := new(credentials.Config). SetType("ram_role_arn"). SetAccessKeyId(config.AccessKeyID). SetAccessKeySecret(config.AccessKeySecret). SetRoleArn(config.RoleArn). SetRoleSessionName("topfans-asset-delete"). SetPolicy(""). SetRoleSessionExpiration(3600) provider, err := credentials.NewCredential(credConfig) if err != nil { return fmt.Errorf("创建凭证提供器失败: %w", err) } cred, err := provider.GetCredential() if err != nil { return fmt.Errorf("获取临时凭证失败: %w", err) } endpoint := fmt.Sprintf("https://oss-%s.aliyuncs.com", config.Region) client, err := oss.New(endpoint, *cred.AccessKeyId, *cred.AccessKeySecret, oss.SecurityToken(*cred.SecurityToken)) if err != nil { return fmt.Errorf("创建OSS客户端失败: %w", err) } bucket, err := client.Bucket(config.BucketName) if err != nil { return fmt.Errorf("获取Bucket失败: %w", err) } if err := bucket.DeleteObject(ossKey); err != nil { return fmt.Errorf("删除OSS对象失败: %w", err) } logger.Logger.Info("删除OSS对象成功", zap.String("oss_key", ossKey)) return nil } // GenerateOSSKey 生成 OSS 对象键(路径) // 格式: asset/{user_id}/{star_id}/covers/{asset_id}_{timestamp}.png func GenerateOSSKey(userID, starID, assetID int64) string { timestamp := time.Now().Unix() return fmt.Sprintf("asset/%d/%d/covers/%d_%d.png", userID, starID, assetID, timestamp) }