diff --git a/alioss.go b/alioss.go index e420edf..3b18b8f 100644 --- a/alioss.go +++ b/alioss.go @@ -16,6 +16,7 @@ package storage import ( "bytes" + "context" "errors" "io" "net/url" @@ -23,55 +24,68 @@ import ( "strings" "time" - "github.com/aliyun/aliyun-oss-go-sdk/oss" + "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss" + "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials" ) func wrapAliOSSError(err error) error { if err == nil { return nil } - var svcErr oss.ServiceError + var svcErr *oss.ServiceError if errors.As(err, &svcErr) { return &ErrorWithStatusCode{ Err: err, StatusCode: svcErr.StatusCode, } } - var unexpectedErr oss.UnexpectedStatusCodeError - if errors.As(err, &unexpectedErr) { - return &ErrorWithStatusCode{ - Err: err, - StatusCode: unexpectedErr.Got(), - } - } return err } type aliOSSStorage struct { conf *AliOSSConfig - bucket *oss.Bucket + client *oss.Client } func NewAliOSS(conf *AliOSSConfig) (Storage, error) { - client, err := oss.New(conf.Endpoint, conf.AccessKey, conf.Secret) - if err != nil { - return nil, err - } - - bucket, err := client.Bucket(conf.Bucket) - if err != nil { - return nil, err - } + cfg := oss.LoadDefaultConfig(). + WithCredentialsProvider(credentials.NewStaticCredentialsProvider(conf.AccessKey, conf.Secret)). + WithEndpoint(conf.Endpoint). + WithRegion(aliOSSRegion(conf)) return &aliOSSStorage{ conf: conf, - bucket: bucket, + client: oss.NewClient(cfg), }, nil } +// aliOSSRegion returns the region used for request signing. It prefers an +// explicitly configured region and otherwise derives one from the endpoint, +// which for public OSS endpoints has the form oss-[-internal].aliyuncs.com. +func aliOSSRegion(conf *AliOSSConfig) string { + if conf.Region != "" { + return conf.Region + } + + host := strings.TrimPrefix(conf.Endpoint, "https://") + host = strings.TrimPrefix(host, "http://") + if i := strings.IndexAny(host, "/:"); i >= 0 { + host = host[:i] + } + host = strings.TrimSuffix(host, ".aliyuncs.com") + host = strings.TrimPrefix(host, "oss-") + host = strings.TrimSuffix(host, "-internal") + return host +} + func (s *aliOSSStorage) UploadData(data []byte, storagePath, contentType string) (string, int64, error) { - reader := bytes.NewBuffer(data) - if err := s.bucket.PutObject(storagePath, reader, oss.ContentType(contentType)); err != nil { + _, err := s.client.PutObject(context.Background(), &oss.PutObjectRequest{ + Bucket: oss.Ptr(s.conf.Bucket), + Key: oss.Ptr(storagePath), + Body: bytes.NewReader(data), + ContentType: oss.Ptr(contentType), + }) + if err != nil { return "", 0, wrapAliOSSError(err) } @@ -84,7 +98,12 @@ func (s *aliOSSStorage) UploadFile(filepath, storagePath, contentType string) (s return "", 0, err } - if err = s.bucket.PutObjectFromFile(storagePath, filepath, oss.ContentType(contentType)); err != nil { + _, err = s.client.PutObjectFromFile(context.Background(), &oss.PutObjectRequest{ + Bucket: oss.Ptr(s.conf.Bucket), + Key: oss.Ptr(storagePath), + ContentType: oss.Ptr(contentType), + }, filepath) + if err != nil { return "", 0, wrapAliOSSError(err) } @@ -100,34 +119,41 @@ func (s *aliOSSStorage) location(storagePath string) string { func (s *aliOSSStorage) ListObjects(prefix string) ([]string, error) { var objects []string - marker := oss.Marker("") + var continuationToken *string for { - lor, err := s.bucket.ListObjects(oss.Prefix(prefix), marker) + lor, err := s.client.ListObjectsV2(context.Background(), &oss.ListObjectsV2Request{ + Bucket: oss.Ptr(s.conf.Bucket), + Prefix: oss.Ptr(prefix), + ContinuationToken: continuationToken, + }) if err != nil { return nil, wrapAliOSSError(err) } - for _, object := range lor.Objects { - objects = append(objects, object.Key) + for _, object := range lor.Contents { + objects = append(objects, oss.ToString(object.Key)) } if !lor.IsTruncated { break } - marker = oss.Marker(lor.NextMarker) + continuationToken = lor.NextContinuationToken } return objects, nil } func (s *aliOSSStorage) DownloadData(storagePath string) ([]byte, error) { - reader, err := s.bucket.GetObject(storagePath) + result, err := s.client.GetObject(context.Background(), &oss.GetObjectRequest{ + Bucket: oss.Ptr(s.conf.Bucket), + Key: oss.Ptr(storagePath), + }) if err != nil { return nil, wrapAliOSSError(err) } - defer reader.Close() + defer result.Body.Close() - data, err := io.ReadAll(reader) + data, err := io.ReadAll(result.Body) if err != nil { return nil, wrapAliOSSError(err) } @@ -135,7 +161,11 @@ func (s *aliOSSStorage) DownloadData(storagePath string) ([]byte, error) { } func (s *aliOSSStorage) DownloadFile(filepath, storagePath string) (int64, error) { - if err := s.bucket.GetObjectToFile(storagePath, filepath); err != nil { + _, err := s.client.GetObjectToFile(context.Background(), &oss.GetObjectRequest{ + Bucket: oss.Ptr(s.conf.Bucket), + Key: oss.Ptr(storagePath), + }, filepath) + if err != nil { return 0, wrapAliOSSError(err) } @@ -148,18 +178,33 @@ func (s *aliOSSStorage) DownloadFile(filepath, storagePath string) (int64, error } func (s *aliOSSStorage) GeneratePresignedUrl(storagePath string, expiration time.Duration) (string, error) { - u, err := s.bucket.SignURL(storagePath, oss.HTTPGet, int64(expiration.Seconds())) + result, err := s.client.Presign(context.Background(), &oss.GetObjectRequest{ + Bucket: oss.Ptr(s.conf.Bucket), + Key: oss.Ptr(storagePath), + }, oss.PresignExpires(expiration)) if err != nil { return "", wrapAliOSSError(err) } - return u, nil + return result.URL, nil } func (s *aliOSSStorage) DeleteObject(storagePath string) error { - return wrapAliOSSError(s.bucket.DeleteObject(storagePath)) + _, err := s.client.DeleteObject(context.Background(), &oss.DeleteObjectRequest{ + Bucket: oss.Ptr(s.conf.Bucket), + Key: oss.Ptr(storagePath), + }) + return wrapAliOSSError(err) } func (s *aliOSSStorage) DeleteObjects(storagePaths []string) error { - _, err := s.bucket.DeleteObjects(storagePaths) + objects := make([]oss.DeleteObject, 0, len(storagePaths)) + for _, p := range storagePaths { + objects = append(objects, oss.DeleteObject{Key: oss.Ptr(p)}) + } + + _, err := s.client.DeleteMultipleObjects(context.Background(), &oss.DeleteMultipleObjectsRequest{ + Bucket: oss.Ptr(s.conf.Bucket), + Objects: objects, + }) return wrapAliOSSError(err) } diff --git a/config.go b/config.go index 4c719c6..05202b4 100644 --- a/config.go +++ b/config.go @@ -27,6 +27,7 @@ type Config interface { type AliOSSConfig struct { AccessKey string `yaml:"access_key,omitempty"` Secret string `yaml:"secret,omitempty"` + Region string `yaml:"region,omitempty"` // optional; derived from endpoint when empty Endpoint string `yaml:"endpoint,omitempty"` Bucket string `yaml:"bucket,omitempty"` } diff --git a/error_test.go b/error_test.go index 9649c94..af833e2 100644 --- a/error_test.go +++ b/error_test.go @@ -24,7 +24,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" smithyhttp "github.com/aws/smithy-go/transport/http" - "github.com/aliyun/aliyun-oss-go-sdk/oss" + "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss" "github.com/stretchr/testify/require" "google.golang.org/api/googleapi" ) @@ -173,24 +173,16 @@ func TestWrapAliOSSError(t *testing.T) { }) t.Run("ServiceError", func(t *testing.T) { - inner := oss.ServiceError{StatusCode: 403, Code: "AccessDenied", Message: "denied"} + inner := &oss.ServiceError{StatusCode: 403, Code: "AccessDenied", Message: "denied"} requireStatus(t, wrapAliOSSError(inner), 403, inner) }) t.Run("wrapped ServiceError", func(t *testing.T) { - inner := oss.ServiceError{StatusCode: 404, Code: "NoSuchKey"} + inner := &oss.ServiceError{StatusCode: 404, Code: "NoSuchKey"} wrapped := fmt.Errorf("get: %w", inner) requireStatus(t, wrapAliOSSError(wrapped), 404, inner) }) - t.Run("UnexpectedStatusCodeError", func(t *testing.T) { - // CheckRespCode returns an UnexpectedStatusCodeError when respCode is not in allowed. - inner := oss.CheckRespCode(500, []int{200}) - require.Error(t, inner) - var sce *ErrorWithStatusCode - require.ErrorAs(t, wrapAliOSSError(inner), &sce) - require.Equal(t, 500, sce.StatusCode) - }) t.Run("plain error passes through", func(t *testing.T) { plain := errors.New("eof") diff --git a/go.mod b/go.mod index 093fc2a..b2b658b 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( cloud.google.com/go/storage v1.62.3 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.7.0 - github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible + github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.5.1 github.com/aws/aws-sdk-go-v2 v1.41.12 github.com/aws/aws-sdk-go-v2/config v1.32.23 github.com/aws/aws-sdk-go-v2/credentials v1.19.22 diff --git a/go.sum b/go.sum index 7bc4eec..f9dc93b 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0 github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.57.0/go.mod h1:dzcEjy1WJ0Q4u9twNR3LcLhNoYMRCrMCMafpxa0TjPQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 h1:RoO5+d7uCmDqovLrHCr2/BuViUXvdcrNxyNM1pN9dDQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0/go.mod h1:YqwkQPrWSC7+byyc1VlKbWLBF5JsW5IoL6xUkemYSXk= -github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= -github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.5.1 h1:vtiFd0hhPAbyYJjztl0wYUq/PqEGkIlDmVuTIy6zw8Y= +github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.5.1/go.mod h1:FTzydeQVmR24FI0D6XWUOMKckjXehM/jgMn1xC+DA9M= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/aws/aws-sdk-go-v2 v1.41.12 h1:DIKX2c31ekm9RA2D9FBj1EWXx++9AdAqRw+e78Tq2Ck=