99 lines
2.8 KiB
Go
99 lines
2.8 KiB
Go
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
|
||
}
|