Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/config"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/servertiming"
|
||||
"github.com/Wei-Shaw/sub2api/internal/service"
|
||||
)
|
||||
|
||||
// S3ImageStorage 用 S3 兼容对象存储实现 service.ImageStorage。
|
||||
type S3ImageStorage struct {
|
||||
client *s3.Client
|
||||
bucket string
|
||||
publicBaseURL string
|
||||
presignExpiry time.Duration
|
||||
}
|
||||
|
||||
var _ service.ImageStorage = (*S3ImageStorage)(nil)
|
||||
|
||||
// NewS3ImageStorage 依据配置构造 S3 图片存储(调用方应先确认 cfg.Active())。
|
||||
func NewS3ImageStorage(ctx context.Context, cfg *config.ImageStorageConfig) (*S3ImageStorage, error) {
|
||||
client, err := newS3Client(ctx, s3ClientParams{
|
||||
Endpoint: cfg.Endpoint,
|
||||
Region: cfg.Region,
|
||||
AccessKeyID: cfg.AccessKeyID,
|
||||
SecretAccessKey: cfg.SecretAccessKey,
|
||||
ForcePathStyle: cfg.ForcePathStyle,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expiry := time.Duration(cfg.PresignExpiry) * time.Hour
|
||||
if expiry <= 0 {
|
||||
expiry = 24 * time.Hour
|
||||
}
|
||||
|
||||
return &S3ImageStorage{
|
||||
client: client,
|
||||
bucket: cfg.Bucket,
|
||||
publicBaseURL: strings.TrimRight(cfg.PublicBaseURL, "/"),
|
||||
presignExpiry: expiry,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Save 上传图片字节,返回可访问 URL:配了 public_base_url 则返回公开直链,否则返回 presigned 临时链接。
|
||||
func (s *S3ImageStorage) Save(ctx context.Context, key, contentType string, data []byte) (string, error) {
|
||||
finish := servertiming.ObserveDependency(ctx, "s3")
|
||||
_, err := s.client.PutObject(ctx, &s3.PutObjectInput{
|
||||
Bucket: &s.bucket,
|
||||
Key: &key,
|
||||
Body: bytes.NewReader(data),
|
||||
ContentType: &contentType,
|
||||
})
|
||||
finish()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("S3 PutObject: %w", err)
|
||||
}
|
||||
|
||||
if s.publicBaseURL != "" {
|
||||
return s.publicBaseURL + "/" + strings.TrimLeft(key, "/"), nil
|
||||
}
|
||||
|
||||
presignClient := s3.NewPresignClient(s.client)
|
||||
result, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &s.bucket,
|
||||
Key: &key,
|
||||
}, s3.WithPresignExpires(s.presignExpiry))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("presign url: %w", err)
|
||||
}
|
||||
return result.URL, nil
|
||||
}
|
||||
Reference in New Issue
Block a user