31
internal/storage/client.go
Normal file
31
internal/storage/client.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.apinb.com/ops/files/internal/config"
|
||||
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
|
||||
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
client *oss.Client
|
||||
bucket string
|
||||
publicBaseURL string
|
||||
presignTTL time.Duration
|
||||
}
|
||||
|
||||
func New(cfg config.ObjectStorageConf) (*Client, error) {
|
||||
ossConfig := oss.LoadDefaultConfig().
|
||||
WithRegion(cfg.Region).
|
||||
WithEndpoint(cfg.Endpoint).
|
||||
WithCredentialsProvider(credentials.NewStaticCredentialsProvider(cfg.AccessKeyID, cfg.AccessKeySecret)).
|
||||
WithSignatureVersion(oss.SignatureVersionV4)
|
||||
|
||||
return &Client{
|
||||
client: oss.NewClient(ossConfig),
|
||||
bucket: cfg.Bucket,
|
||||
publicBaseURL: cfg.PublicBaseURL,
|
||||
presignTTL: time.Duration(cfg.PresignTTLSeconds) * time.Second,
|
||||
}, nil
|
||||
}
|
||||
76
internal/storage/object.go
Normal file
76
internal/storage/object.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
|
||||
)
|
||||
|
||||
type ObjectInfo struct {
|
||||
Size int64
|
||||
ETag string
|
||||
LastModified time.Time
|
||||
}
|
||||
|
||||
func (c *Client) Stat(ctx context.Context, objectKey string) (ObjectInfo, error) {
|
||||
if strings.TrimSpace(objectKey) == "" {
|
||||
return ObjectInfo{}, fmt.Errorf("对象键不能为空")
|
||||
}
|
||||
|
||||
result, err := c.client.GetObjectMeta(ctx, &oss.GetObjectMetaRequest{
|
||||
Bucket: oss.Ptr(c.bucket),
|
||||
Key: oss.Ptr(objectKey),
|
||||
})
|
||||
if err != nil {
|
||||
return ObjectInfo{}, fmt.Errorf("查询对象元数据: %w", err)
|
||||
}
|
||||
if result == nil {
|
||||
return ObjectInfo{}, fmt.Errorf("对象元数据为空")
|
||||
}
|
||||
if result.ContentLength < 0 {
|
||||
return ObjectInfo{}, fmt.Errorf("对象大小无效: %d", result.ContentLength)
|
||||
}
|
||||
if result.ETag == nil {
|
||||
return ObjectInfo{}, fmt.Errorf("对象 ETag 缺失")
|
||||
}
|
||||
etag := strings.TrimSpace(strings.Trim(strings.TrimSpace(*result.ETag), "\""))
|
||||
if etag == "" {
|
||||
return ObjectInfo{}, fmt.Errorf("对象 ETag 无效")
|
||||
}
|
||||
if result.LastModified == nil || result.LastModified.IsZero() {
|
||||
return ObjectInfo{}, fmt.Errorf("对象最后修改时间缺失")
|
||||
}
|
||||
|
||||
return ObjectInfo{
|
||||
Size: result.ContentLength,
|
||||
ETag: etag,
|
||||
LastModified: *result.LastModified,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) Delete(ctx context.Context, objectKey string) error {
|
||||
if strings.TrimSpace(objectKey) == "" {
|
||||
return fmt.Errorf("对象键不能为空")
|
||||
}
|
||||
|
||||
_, err := c.client.DeleteObject(ctx, &oss.DeleteObjectRequest{
|
||||
Bucket: oss.Ptr(c.bucket),
|
||||
Key: oss.Ptr(objectKey),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("删除对象: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) PublicURL(objectKey string) string {
|
||||
return strings.TrimRight(c.publicBaseURL, "/") + "/" + strings.TrimLeft(objectKey, "/")
|
||||
}
|
||||
|
||||
func (c *Client) Bucket() string {
|
||||
return c.bucket
|
||||
}
|
||||
43
internal/storage/presign.go
Normal file
43
internal/storage/presign.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
|
||||
)
|
||||
|
||||
type UploadInstruction struct {
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
|
||||
func (c *Client) PresignPut(ctx context.Context, objectKey, contentType string) (UploadInstruction, error) {
|
||||
if strings.TrimSpace(objectKey) == "" {
|
||||
return UploadInstruction{}, fmt.Errorf("对象键不能为空")
|
||||
}
|
||||
if strings.TrimSpace(contentType) == "" {
|
||||
return UploadInstruction{}, fmt.Errorf("内容类型不能为空")
|
||||
}
|
||||
|
||||
result, err := c.client.Presign(ctx, &oss.PutObjectRequest{
|
||||
Bucket: oss.Ptr(c.bucket),
|
||||
Key: oss.Ptr(objectKey),
|
||||
ContentType: oss.Ptr(contentType),
|
||||
ForbidOverwrite: oss.Ptr("true"),
|
||||
}, oss.PresignExpires(c.presignTTL))
|
||||
if err != nil {
|
||||
return UploadInstruction{}, fmt.Errorf("生成上传预签名: %w", err)
|
||||
}
|
||||
|
||||
return UploadInstruction{
|
||||
Method: result.Method,
|
||||
URL: result.URL,
|
||||
Headers: result.SignedHeaders,
|
||||
ExpiresAt: result.Expiration,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user