2026-05-02 13:02:38 +08:00

89 lines
2.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package tencentcos 封装腾讯云 COS 对象上传能力。
package tencentcos
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
cos "github.com/tencentyun/cos-go-sdk-v5"
)
// Config 是 gateway 上传文件所需的 COS 最小配置。
type Config struct {
SecretID string
SecretKey string
Bucket string
Region string
AccessURL string
}
// Uploader 负责把 gateway 收到的文件流写入 COS并返回 CDN/自定义域名访问地址。
type Uploader struct {
client *cos.Client
accessURL string
}
// NewUploader 按 bucket + region 装配 COS SDK client。
func NewUploader(cfg Config, transport http.RoundTripper) (*Uploader, error) {
cfg.SecretID = strings.TrimSpace(cfg.SecretID)
cfg.SecretKey = strings.TrimSpace(cfg.SecretKey)
cfg.Bucket = strings.TrimSpace(cfg.Bucket)
cfg.Region = strings.TrimSpace(cfg.Region)
cfg.AccessURL = strings.TrimRight(strings.TrimSpace(cfg.AccessURL), "/")
if cfg.SecretID == "" || cfg.SecretKey == "" || cfg.Bucket == "" || cfg.Region == "" || cfg.AccessURL == "" {
return nil, fmt.Errorf("tencent cos config is incomplete")
}
if _, err := url.ParseRequestURI(cfg.AccessURL); err != nil {
return nil, fmt.Errorf("tencent cos access_url is invalid: %w", err)
}
bucketURL, err := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", cfg.Bucket, cfg.Region))
if err != nil {
return nil, fmt.Errorf("tencent cos bucket url is invalid: %w", err)
}
if transport == nil {
transport = http.DefaultTransport
}
client := cos.NewClient(&cos.BaseURL{BucketURL: bucketURL}, &http.Client{
Timeout: 30 * time.Second,
Transport: &cos.AuthorizationTransport{
SecretID: cfg.SecretID,
SecretKey: cfg.SecretKey,
Transport: transport,
},
})
return &Uploader{client: client, accessURL: cfg.AccessURL}, nil
}
// PutObject 上传对象并返回对外访问 URL。
func (u *Uploader) PutObject(ctx context.Context, key string, reader io.Reader, sizeBytes int64, contentType string) (string, error) {
key = strings.TrimLeft(strings.TrimSpace(key), "/")
if key == "" || reader == nil || sizeBytes <= 0 {
return "", fmt.Errorf("object upload input is invalid")
}
options := &cos.ObjectPutOptions{
ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{
ContentLength: sizeBytes,
ContentType: strings.TrimSpace(contentType),
},
}
if _, err := u.client.Object.Put(ctx, key, reader, options); err != nil {
return "", err
}
publicURL, err := url.JoinPath(u.accessURL, key)
if err != nil {
return "", fmt.Errorf("build public object url: %w", err)
}
return publicURL, nil
}