package util import ( "context" "epic/internal/consts" "fmt" "io" "net/http" "os" "path/filepath" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/s3" ) // DownloadAndUploadToOSS 下载网络图片并上传到OSS,返回OSS路径 // imageUrl: 网络图片完整URL // 返回: OSS上的图片URL,或错误 func DownloadAndUploadToOSS(ctx context.Context, imageUrl string) (string, error) { // 1. 下载 imageUrl 到本地临时文件 resp, err := http.Get(imageUrl) if err != nil { return "", fmt.Errorf("failed to download image: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("failed to download image: status %d", resp.StatusCode) } tmpFile, err := os.CreateTemp("", "ossimg-*.tmp") if err != nil { return "", fmt.Errorf("failed to create temp file: %w", err) } tmpFilePath := tmpFile.Name() defer func() { tmpFile.Close() os.Remove(tmpFilePath) }() _, err = io.Copy(tmpFile, resp.Body) if err != nil { return "", fmt.Errorf("failed to save image to temp file: %w", err) } // 2. 上传临时文件到OSS,获取OSS路径 accessKey := consts.S3AccessKey secretKey := consts.S3SecretKey bucket := consts.S3Bucket region := consts.S3Region endpoint := consts.S3Endpoint if accessKey == "" || secretKey == "" || bucket == "" || endpoint == "" { return "", fmt.Errorf("missing S3 credentials or bucket info in consts") } customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) { if service == s3.ServiceID { return aws.Endpoint{URL: endpoint}, nil } return aws.Endpoint{}, fmt.Errorf("unknown service requested") }) customProvider := credentials.NewStaticCredentialsProvider(accessKey, secretKey, "") cfg, err := config.LoadDefaultConfig(ctx, config.WithCredentialsProvider(customProvider), config.WithEndpointResolverWithOptions(customResolver), ) if err != nil { return "", fmt.Errorf("failed to load S3 config: %w", err) } cfg.Region = region s3Client := s3.NewFromConfig(cfg) // 生成唯一的 object key fileName := filepath.Base(tmpFilePath) objectKey := fmt.Sprintf("images/%d_%s", time.Now().UnixNano(), fileName) tmpFile.Seek(0, io.SeekStart) _, err = tmpFile.Stat() if err != nil { return "", fmt.Errorf("failed to stat temp file: %w", err) } // 上传 _, err = s3Client.PutObject(ctx, &s3.PutObjectInput{ Bucket: aws.String(bucket), Key: aws.String(objectKey), Body: tmpFile, }) if err != nil { return "", fmt.Errorf("failed to upload to S3: %w", err) } // 4. 返回OSS路径(拼接URL) ossUrl := fmt.Sprintf("%s/%s/%s", endpoint, bucket, objectKey) return ossUrl, nil }