Files
goalfylearning-admin/internal/oss/s3.go

101 lines
2.3 KiB
Go

package oss
import (
"bytes"
"context"
"fmt"
"sync"
"time"
"goalfymax-admin/internal/config"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
type s3Client struct {
client *s3.Client
presign *s3.PresignClient
cfg config.OssConfig
}
var (
clientOnce sync.Once
c *s3Client
)
func initClient() error {
var initErr error
clientOnce.Do(func() {
cfg := config.GetConfig().Oss
if cfg.Region == "" || cfg.Bucket == "" || cfg.AccessKeyID == "" || cfg.AccessKeySecret == "" {
initErr = fmt.Errorf("OSS未配置: region/bucket/ak/sk 不能为空")
return
}
awsCfg := aws.Config{
Region: cfg.Region,
Credentials: credentials.NewStaticCredentialsProvider(cfg.AccessKeyID, cfg.AccessKeySecret, ""),
}
s3c := s3.NewFromConfig(awsCfg)
c = &s3Client{
client: s3c,
presign: s3.NewPresignClient(s3c),
cfg: cfg,
}
})
return initErr
}
// GetPresignedGetURL 生成S3对象的预签名下载URL
func GetPresignedGetURL(ctx context.Context, key string) (string, error) {
if err := initClient(); err != nil {
return "", err
}
expire := c.cfg.PresignUrlExpire
if expire <= 0 {
expire = 10 * time.Minute
}
req, err := c.presign.PresignGetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(c.cfg.Bucket),
Key: aws.String(key),
}, func(po *s3.PresignOptions) {
po.Expires = expire
})
if err != nil {
return "", fmt.Errorf("生成预签名URL失败: %w", err)
}
return req.URL, nil
}
// DownloadFileContent 从S3下载文件内容和MIME类型
func DownloadFileContent(ctx context.Context, key string) ([]byte, string, error) {
if err := initClient(); err != nil {
return nil, "", err
}
resp, err := c.client.GetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(c.cfg.Bucket),
Key: aws.String(key),
})
if err != nil {
return nil, "", fmt.Errorf("从S3下载文件失败: %w", err)
}
defer resp.Body.Close()
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(resp.Body)
if err != nil {
return nil, "", fmt.Errorf("读取文件内容失败: %w", err)
}
mimeType := "application/octet-stream" // 默认MIME类型
if resp.ContentType != nil && *resp.ContentType != "" {
mimeType = *resp.ContentType
}
return buf.Bytes(), mimeType, nil
}