dragonfly/pkg/objectstorage/oss.go

257 lines
7.2 KiB
Go

/*
* Copyright 2022 The Dragonfly Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package objectstorage
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"strconv"
"time"
aliyunoss "github.com/aliyun/aliyun-oss-go-sdk/oss"
"github.com/go-http-utils/headers"
)
type oss struct {
// OSS client.
client *aliyunoss.Client
}
// New oss instance.
func newOSS(region, endpoint, accessKey, secretKey string, httpClient *http.Client) (ObjectStorage, error) {
client, err := aliyunoss.New(endpoint, accessKey, secretKey, aliyunoss.Region(region), aliyunoss.HTTPClient(httpClient))
if err != nil {
return nil, fmt.Errorf("new oss client failed: %s", err)
}
return &oss{
client: client,
}, nil
}
// GetBucketMetadata returns metadata of bucket.
func (o *oss) GetBucketMetadata(ctx context.Context, bucketName string) (*BucketMetadata, error) {
resp, err := o.client.GetBucketInfo(bucketName)
if err != nil {
return nil, err
}
return &BucketMetadata{
Name: resp.BucketInfo.Name,
CreateAt: resp.BucketInfo.CreationDate,
}, nil
}
// CreateBucket creates bucket of object storage.
func (o *oss) CreateBucket(ctx context.Context, bucketName string) error {
return o.client.CreateBucket(bucketName)
}
// DeleteBucket deletes bucket of object storage.
func (o *oss) DeleteBucket(ctx context.Context, bucketName string) error {
return o.client.DeleteBucket(bucketName)
}
// DeleteBucket deletes bucket of object storage.
func (o *oss) ListBucketMetadatas(ctx context.Context) ([]*BucketMetadata, error) {
resp, err := o.client.ListBuckets()
if err != nil {
return nil, err
}
var metadatas []*BucketMetadata
for _, bucket := range resp.Buckets {
metadatas = append(metadatas, &BucketMetadata{
Name: bucket.Name,
CreateAt: bucket.CreationDate,
})
}
return metadatas, nil
}
// GetObjectMetadata returns metadata of object.
func (o *oss) GetObjectMetadata(ctx context.Context, bucketName, objectKey string) (*ObjectMetadata, bool, error) {
bucket, err := o.client.Bucket(bucketName)
if err != nil {
return nil, false, err
}
header, err := bucket.GetObjectDetailedMeta(objectKey)
if err != nil {
var serr aliyunoss.ServiceError
if errors.As(err, &serr) && serr.StatusCode == http.StatusNotFound {
return nil, false, nil
}
return nil, false, err
}
contentLength, err := strconv.ParseInt(header.Get(headers.ContentLength), 10, 64)
if err != nil {
return nil, false, err
}
lastModifiedTime, err := time.Parse(http.TimeFormat, header.Get(aliyunoss.HTTPHeaderLastModified))
if err != nil {
return nil, false, err
}
return &ObjectMetadata{
Key: objectKey,
ContentDisposition: header.Get(headers.ContentDisposition),
ContentEncoding: header.Get(headers.ContentEncoding),
ContentLanguage: header.Get(headers.ContentLanguage),
ContentLength: contentLength,
ContentType: header.Get(headers.ContentType),
ETag: header.Get(headers.ETag),
Digest: header.Get(aliyunoss.HTTPHeaderOssMetaPrefix + MetaDigest),
LastModifiedTime: lastModifiedTime,
StorageClass: o.getStorageClass(header.Get(aliyunoss.HTTPHeaderOssStorageClass)),
}, true, nil
}
// GetObjectMetadatas returns the metadatas of the objects.
func (o *oss) GetObjectMetadatas(ctx context.Context, bucketName, prefix, marker, delimiter string, limit int64) (*ObjectMetadatas, error) {
bucket, err := o.client.Bucket(bucketName)
if err != nil {
return nil, err
}
if limit == 0 {
limit = DefaultGetObjectMetadatasLimit
}
resp, err := bucket.ListObjects(aliyunoss.Prefix(prefix), aliyunoss.Marker(marker), aliyunoss.Delimiter(delimiter), aliyunoss.MaxKeys(int(limit)))
if err != nil {
return nil, err
}
metadatas := make([]*ObjectMetadata, 0, len(resp.Objects))
for _, object := range resp.Objects {
metadatas = append(metadatas, &ObjectMetadata{
Key: object.Key,
ETag: object.ETag,
ContentLength: object.Size,
LastModifiedTime: object.LastModified,
StorageClass: o.getStorageClass(object.StorageClass),
})
}
return &ObjectMetadatas{
Metadatas: metadatas,
CommonPrefixes: resp.CommonPrefixes,
}, nil
}
// GetOject returns data of object.
func (o *oss) GetOject(ctx context.Context, bucketName, objectKey string) (io.ReadCloser, error) {
bucket, err := o.client.Bucket(bucketName)
if err != nil {
return nil, err
}
return bucket.GetObject(bucketName)
}
// PutObject puts data of object.
func (o *oss) PutObject(ctx context.Context, bucketName, objectKey, digest string, reader io.Reader) error {
bucket, err := o.client.Bucket(bucketName)
if err != nil {
return err
}
meta := aliyunoss.Meta(MetaDigest, digest)
return bucket.PutObject(objectKey, reader, meta)
}
// DeleteObject deletes data of object.
func (o *oss) DeleteObject(ctx context.Context, bucketName, objectKey string) error {
bucket, err := o.client.Bucket(bucketName)
if err != nil {
return err
}
return bucket.DeleteObject(objectKey)
}
// IsObjectExist returns whether the object exists.
func (o *oss) IsObjectExist(ctx context.Context, bucketName, objectKey string) (bool, error) {
bucket, err := o.client.Bucket(bucketName)
if err != nil {
return false, err
}
return bucket.IsObjectExist(objectKey)
}
// IsBucketExist returns whether the bucket exists.
func (o *oss) IsBucketExist(ctx context.Context, bucketName string) (bool, error) {
return o.client.IsBucketExist(bucketName)
}
// CopyObject copy object from source to destination.
func (o *oss) CopyObject(ctx context.Context, bucketName, sourceObjectKey, destinationObjectKey string) error {
bucket, err := o.client.Bucket(bucketName)
if err != nil {
return err
}
_, err = bucket.CopyObject(sourceObjectKey, destinationObjectKey)
return err
}
// GetSignURL returns sign url of object.
func (o *oss) GetSignURL(ctx context.Context, bucketName, objectKey string, method Method, expire time.Duration) (string, error) {
var ossHTTPMethod aliyunoss.HTTPMethod
switch method {
case MethodGet:
ossHTTPMethod = aliyunoss.HTTPGet
case MethodPut:
ossHTTPMethod = aliyunoss.HTTPPut
case MethodHead:
ossHTTPMethod = aliyunoss.HTTPHead
case MethodPost:
ossHTTPMethod = aliyunoss.HTTPPost
case MethodDelete:
ossHTTPMethod = aliyunoss.HTTPDelete
case MethodList:
ossHTTPMethod = aliyunoss.HTTPGet
default:
return "", fmt.Errorf("not support method %s", method)
}
bucket, err := o.client.Bucket(bucketName)
if err != nil {
return "", err
}
return bucket.SignURL(objectKey, ossHTTPMethod, int64(expire.Seconds()))
}
// getStorageClass returns the default storage class if the input is empty.
func (o *oss) getStorageClass(storageClass string) string {
if storageClass == "" {
storageClass = string(aliyunoss.StorageStandard)
}
return storageClass
}