github.com/sandwich-go/boost@v1.3.29/misc/cloud/storage.go (about)

     1  package cloud
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"github.com/minio/minio-go/v7"
     7  	"github.com/minio/minio-go/v7/pkg/credentials"
     8  	"github.com/sandwich-go/boost/xpanic"
     9  	"io"
    10  	"net/url"
    11  	"strings"
    12  )
    13  
    14  var (
    15  	creators        = map[StorageType]storageCreator{}
    16  	emptyObjectInfo = ObjectInfo{}
    17  )
    18  
    19  func register(st StorageType, creator storageCreator) { creators[st] = creator }
    20  
    21  type (
    22  	// StorageType 存储器类型
    23  	StorageType string
    24  	// storageCreator 存储器创建
    25  	storageCreator = func(accessKeyID string, secretAccessKey string, bucket string, opts ...StorageOption) (Storage, error)
    26  	// endpointGetter endpoint 获取器
    27  	endpointGetter = func(*StorageOptions) (ep string, err error)
    28  	// objectNameResolver 解析 object name
    29  	objectNameResolver = func(*url.URL) (string, error)
    30  )
    31  
    32  // New 新建 Storage
    33  func New(st StorageType, accessKeyID string, secretAccessKey string, bucket string, opts ...StorageOption) (Storage, error) {
    34  	creator, ok := creators[st]
    35  	if !ok {
    36  		return nil, ErrUnknownStorageType
    37  	}
    38  	return creator(accessKeyID, secretAccessKey, bucket, opts...)
    39  }
    40  
    41  // MustNew 新建 Storage,失败会 panic
    42  func MustNew(st StorageType, accessKeyID string, secretAccessKey string, bucket string, opts ...StorageOption) Storage {
    43  	s, err := New(st, accessKeyID, secretAccessKey, bucket, opts...)
    44  	xpanic.WhenError(err)
    45  	return s
    46  }
    47  
    48  type baseStorage struct {
    49  	cli      *minio.Client
    50  	bucket   string
    51  	resolver objectNameResolver
    52  	spec     *StorageOptions
    53  }
    54  
    55  func newBaseBucket(accessKeyID string, secretAccessKey string, bucket string, getter endpointGetter, opts ...StorageOption) (*baseStorage, error) {
    56  	spec := NewStorageOptions(opts...)
    57  	ep, err := getter(spec)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	var cli *minio.Client
    62  	cli, err = minio.New(ep, &minio.Options{
    63  		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
    64  		Secure: false,
    65  	})
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	return &baseStorage{cli: cli, bucket: bucket, spec: spec}, nil
    70  }
    71  
    72  func (c *baseStorage) setObjectNameResolver(resolver objectNameResolver) {
    73  	c.resolver = resolver
    74  }
    75  
    76  func (c baseStorage) ResolveObjectName(rawUrl string) (string, error) {
    77  	u, err := url.Parse(rawUrl)
    78  	if err != nil {
    79  		return "", err
    80  	}
    81  	if c.resolver != nil {
    82  		return c.resolver(u)
    83  	}
    84  	return strings.Trim(u.Path, "/"), nil
    85  }
    86  
    87  func (c baseStorage) String() string { return c.bucket }
    88  func (c baseStorage) DelObject(ctx context.Context, objName string) error {
    89  	return c.cli.RemoveObject(ctx, c.bucket, objName, minio.RemoveObjectOptions{})
    90  }
    91  
    92  func (c baseStorage) PutObject(ctx context.Context, objName string, reader io.Reader, objSize int, opts ...PutOption) (err error) {
    93  	spec := NewPutOptions(opts...)
    94  	op := minio.PutObjectOptions{
    95  		ContentType:        spec.ContentType,
    96  		ContentDisposition: spec.ContentDisposition,
    97  		CacheControl:       spec.CacheControl,
    98  	}
    99  	if objSize == 0 {
   100  		op.DisableMultipart = true
   101  	}
   102  	_, err = c.cli.PutObject(ctx, c.bucket, objName, reader, int64(objSize), op)
   103  	return
   104  }
   105  
   106  func (c baseStorage) StatObject(ctx context.Context, objName string) (ObjectInfo, error) {
   107  	info, err := c.cli.StatObject(ctx, c.bucket, objName, minio.StatObjectOptions{})
   108  	if err != nil {
   109  		return emptyObjectInfo, err
   110  	}
   111  	return ObjectInfo{
   112  		ETag:         info.ETag,
   113  		Key:          info.Key,
   114  		LastModified: info.LastModified,
   115  		Size:         info.Size,
   116  		ContentType:  info.ContentType,
   117  	}, nil
   118  }
   119  
   120  func (c baseStorage) ListObjects(ctx context.Context, prefix string) <-chan ObjectInfo {
   121  	ch0 := c.cli.ListObjects(ctx, c.bucket, minio.ListObjectsOptions{
   122  		Prefix: prefix,
   123  	})
   124  	ch1 := make(chan ObjectInfo)
   125  	go func() {
   126  		defer close(ch1)
   127  		for info := range ch0 {
   128  			ch1 <- ObjectInfo{
   129  				ETag:         info.ETag,
   130  				Key:          info.Key,
   131  				LastModified: info.LastModified,
   132  				Size:         info.Size,
   133  				ContentType:  info.ContentType,
   134  			}
   135  		}
   136  	}()
   137  	return ch1
   138  }
   139  
   140  func (c baseStorage) GetObject(ctx context.Context, objName string) (io.Reader, error) {
   141  	obj, err := c.cli.GetObject(ctx, c.bucket, objName, minio.GetObjectOptions{})
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	defer func(o io.ReadCloser) {
   146  		_ = o.Close()
   147  	}(obj)
   148  	buf := new(bytes.Buffer)
   149  	_, err = io.Copy(buf, obj)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	return buf, nil
   154  }
   155  
   156  func (c baseStorage) CopyObject(ctx context.Context, destObjName, srcObjName string) error {
   157  	_, err := c.cli.CopyObject(ctx, minio.CopyDestOptions{
   158  		Bucket: c.bucket,
   159  		Object: destObjName,
   160  	}, minio.CopySrcOptions{
   161  		Bucket: c.bucket,
   162  		Object: srcObjName,
   163  	})
   164  	return err
   165  }