github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/objects/directory.go (about)

     1  package objects
     2  
     3  import (
     4  	"container/list"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	"github.com/qiniu/go-sdk/v7/internal/context"
     9  	"github.com/qiniu/go-sdk/v7/storagev2/apis"
    10  )
    11  
    12  type (
    13  	// 目录
    14  	Directory struct {
    15  		bucket                *Bucket
    16  		prefix, pathSeparator string
    17  	}
    18  
    19  	// 条目
    20  	Entry struct {
    21  		// 目录名称,仅当条目为目录时才有效
    22  		DirectoryName string
    23  
    24  		// 对象元信息,仅当条目为对象时才有效
    25  		Object *ObjectDetails
    26  	}
    27  
    28  	// 列举条目选项
    29  	ListEntriesOptions struct {
    30  		NeedParts bool // 是否需要分片信息
    31  		Recursive bool // 是否递归列举
    32  	}
    33  )
    34  
    35  var SkipDir = filepath.SkipDir
    36  
    37  // 移动目录
    38  func (directory *Directory) MoveTo(ctx context.Context, toBucketName, toPrefix string) error {
    39  	if !strings.HasSuffix(toPrefix, directory.pathSeparator) {
    40  		toPrefix += directory.pathSeparator
    41  	}
    42  	operations := make([]Operation, 0, 16)
    43  	if err := directory.forEachObject(ctx, func(objectDetails *ObjectDetails) {
    44  		toObjectName := toPrefix + strings.TrimPrefix(objectDetails.Name, directory.prefix)
    45  		operations = append(operations, directory.bucket.Object(objectDetails.Name).MoveTo(toBucketName, toObjectName))
    46  	}); err != nil {
    47  		return err
    48  	}
    49  	return directory.bucket.objectsManager.Batch(ctx, operations, nil)
    50  }
    51  
    52  // 复制目录
    53  func (directory *Directory) CopyTo(ctx context.Context, toBucketName, toPrefix string) error {
    54  	if !strings.HasSuffix(toPrefix, directory.pathSeparator) {
    55  		toPrefix += directory.pathSeparator
    56  	}
    57  	operations := make([]Operation, 0, 16)
    58  	if err := directory.forEachObject(ctx, func(objectDetails *ObjectDetails) {
    59  		toObjectName := toPrefix + strings.TrimPrefix(objectDetails.Name, directory.prefix)
    60  		operations = append(operations, directory.bucket.Object(objectDetails.Name).CopyTo(toBucketName, toObjectName))
    61  	}); err != nil {
    62  		return err
    63  	}
    64  	return directory.bucket.objectsManager.Batch(ctx, operations, nil)
    65  }
    66  
    67  // 删除目录
    68  func (directory *Directory) Delete(ctx context.Context) error {
    69  	operations := make([]Operation, 0, 16)
    70  	if err := directory.forEachObject(ctx, func(objectDetails *ObjectDetails) {
    71  		operations = append(operations, directory.bucket.Object(objectDetails.Name).Delete())
    72  	}); err != nil {
    73  		return err
    74  	}
    75  	return directory.bucket.objectsManager.Batch(ctx, operations, nil)
    76  }
    77  
    78  func (directory *Directory) forEachObject(ctx context.Context, each func(*ObjectDetails)) error {
    79  	lister := directory.bucket.List(ctx, &ListObjectsOptions{Prefix: directory.prefix})
    80  	defer lister.Close()
    81  
    82  	var objectDetails ObjectDetails
    83  	for lister.Next(&objectDetails) {
    84  		each(&objectDetails)
    85  	}
    86  
    87  	return lister.Error()
    88  }
    89  
    90  // 列举目录条目
    91  func (directory *Directory) ListEntries(ctx context.Context, options *ListEntriesOptions, f func(*Entry) error) error {
    92  	if options == nil {
    93  		options = &ListEntriesOptions{}
    94  	}
    95  
    96  	directories := list.New()
    97  	directories.PushBack(directory)
    98  	return consumeEntries(ctx, directories, options, f)
    99  }
   100  
   101  func (directory *Directory) listEntries(ctx context.Context, marker string, needParts bool) ([]*Entry, string, error) {
   102  	entries := make([]*Entry, 0, 1024)
   103  	request := apis.GetObjectsRequest{
   104  		Bucket:    directory.bucket.name,
   105  		Prefix:    directory.prefix,
   106  		Delimiter: directory.pathSeparator,
   107  		Marker:    marker,
   108  		NeedParts: needParts,
   109  	}
   110  	response, err := directory.bucket.objectsManager.storage.GetObjects(ctx, &request, nil)
   111  	if err != nil {
   112  		return nil, "", err
   113  	}
   114  	for _, commonPrefix := range response.CommonPrefixes {
   115  		entries = append(entries, &Entry{DirectoryName: commonPrefix})
   116  	}
   117  	for _, item := range response.Items {
   118  		objectDetails := new(ObjectDetails)
   119  		if err = objectDetails.fromListedObjectEntry(&item); err != nil {
   120  			return nil, "", err
   121  		}
   122  		entries = append(entries, &Entry{Object: objectDetails})
   123  	}
   124  	return entries, response.Marker, nil
   125  }
   126  
   127  func consumeEntries(ctx context.Context, directories *list.List, options *ListEntriesOptions, f func(*Entry) error) error {
   128  	var firstElement *list.Element
   129  	for {
   130  		if firstElement = directories.Front(); firstElement == nil {
   131  			break
   132  		}
   133  		directories.Remove(firstElement)
   134  		currentDirectory := firstElement.Value.(*Directory)
   135  
   136  		var (
   137  			entries   []*Entry
   138  			firstPage = true
   139  			marker    string
   140  			err       error
   141  		)
   142  	nextPage:
   143  		for firstPage || marker != "" {
   144  			firstPage = false
   145  			if entries, marker, err = currentDirectory.listEntries(ctx, marker, options.NeedParts); err != nil {
   146  				return err
   147  			} else {
   148  				for _, entry := range entries {
   149  					err := f(entry)
   150  					switch err {
   151  					case nil:
   152  						if options.Recursive && entry.DirectoryName != "" {
   153  							directories.PushBack(&Directory{bucket: currentDirectory.bucket, prefix: entry.DirectoryName, pathSeparator: currentDirectory.pathSeparator})
   154  						}
   155  					case SkipDir:
   156  						if entry.DirectoryName == "" {
   157  							continue nextPage
   158  						}
   159  					default:
   160  						return err
   161  					}
   162  				}
   163  			}
   164  		}
   165  	}
   166  	return nil
   167  }