package storage import ( "context" "fmt" "io" "path" "strings" "github.com/aliyun/aliyun-oss-go-sdk/oss" "kra/internal/biz" "kra/internal/conf" ) type aliyunStorage struct { bucket *oss.Bucket baseURL, prefix string } func newAliyunStorage(config *conf.AdminBackend_ObjectStore) (biz.FileStorage, error) { if config == nil || config.Endpoint == "" || config.Bucket == "" || config.AccessKey == "" || config.SecretKey == "" { return nil, fmt.Errorf("aliyun-oss storage configuration is incomplete") } client, err := oss.New(config.Endpoint, config.AccessKey, config.SecretKey) if err != nil { return nil, err } bucket, err := client.Bucket(config.Bucket) if err != nil { return nil, err } return &aliyunStorage{bucket: bucket, baseURL: strings.TrimSuffix(config.BaseUrl, "/"), prefix: strings.Trim(config.PathPrefix, "/")}, nil } func (s *aliyunStorage) key(name string) string { if s.prefix == "" { return strings.TrimPrefix(name, "/") } return path.Join(s.prefix, strings.TrimPrefix(name, "/")) } func (s *aliyunStorage) unkey(key string) string { return strings.TrimPrefix(strings.TrimPrefix(key, s.prefix), "/") } func (s *aliyunStorage) file(key string, size int64) *biz.StoredFile { name := s.unkey(key) url := s.baseURL + "/" + key return &biz.StoredFile{Name: path.Base(name), Path: name, URL: url, Size: size} } func (s *aliyunStorage) Put(_ context.Context, name string, reader io.Reader) (*biz.StoredFile, error) { key := s.key(name) if err := s.bucket.PutObject(key, reader); err != nil { return nil, err } meta, err := s.bucket.GetObjectDetailedMeta(key) if err != nil { return nil, err } size := int64(0) _, _ = fmt.Sscan(meta.Get("Content-Length"), &size) return s.file(key, size), nil } func (s *aliyunStorage) Open(_ context.Context, name string) (io.ReadCloser, error) { return s.bucket.GetObject(s.key(name)) } func (s *aliyunStorage) Delete(_ context.Context, name string) error { return s.bucket.DeleteObject(s.key(name)) } func (s *aliyunStorage) Compose(ctx context.Context, names []string, destination string) (*biz.StoredFile, string, error) { return composeFiles(ctx, s, names, destination) } func (s *aliyunStorage) DeletePrefix(ctx context.Context, prefix string) error { cursor := "" for { items, next, more, err := s.List(ctx, prefix, cursor, 1000) if err != nil { return err } for _, item := range items { if err = s.Delete(ctx, item.Path); err != nil { return err } } if !more { return nil } cursor = next } } func (s *aliyunStorage) List(_ context.Context, prefix, cursor string, limit int) ([]*biz.StoredFile, string, bool, error) { if limit < 1 || limit > 1000 { limit = 100 } result, err := s.bucket.ListObjects(oss.Prefix(s.key(prefix)), oss.Marker(cursor), oss.MaxKeys(limit)) if err != nil { return nil, "", false, err } items := make([]*biz.StoredFile, 0, len(result.Objects)) for _, object := range result.Objects { item := s.file(object.Key, object.Size) item.LastModified = object.LastModified items = append(items, item) } next := "" if result.IsTruncated { next = result.NextMarker } return items, next, result.IsTruncated, nil }