package storage import ( "context" "errors" "fmt" "github.com/qiniu/go-sdk/v7/auth" "net/url" "strconv" ) // ListItem 为文件列举的返回值 type ListItem struct { // 资源名 Key string `json:"key"` // 上传时间,单位:100纳秒,其值去掉低七位即为Unix时间戳。 PutTime int64 `json:"putTime"` // 文件的HASH值,使用hash值算法计算。 Hash string `json:"hash"` // 资源内容的大小,单位:字节。 Fsize int64 `json:"fsize"` // 资源的 MIME 类型。 MimeType string `json:"mimeType"` /** * 文件上传时设置的endUser */ EndUser string `json:"endUser"` /** * 资源的存储类型 * 0 表示标准存储 * 1 表示低频存储 * 2 表示归档存储 * 3 表示深度归档存储 */ Type int `json:"type"` /** * 文件的存储状态,即禁用状态和启用状态间的的互相转换,请参考:文件状态。 * 0 表示启用 * 1 表示禁用 */ Status int `json:"status"` /** * 文件的 md5 值 */ Md5 string `json:"md5"` } // 接口可能返回空的记录 func (l *ListItem) IsEmpty() (empty bool) { if l == nil { return true } return l.Key == "" && l.Hash == "" && l.Fsize == 0 && l.PutTime == 0 } func (l *ListItem) String() string { str := "" str += fmt.Sprintf("Hash: %s\n", l.Hash) str += fmt.Sprintf("Fsize: %d\n", l.Fsize) str += fmt.Sprintf("PutTime: %d\n", l.PutTime) str += fmt.Sprintf("MimeType: %s\n", l.MimeType) str += fmt.Sprintf("Type: %d\n", l.Type) str += fmt.Sprintf("EndUser: %s\n", l.EndUser) return str } type ListFilesRet struct { Marker string `json:"marker"` Items []ListItem `json:"items"` CommonPrefixes []string `json:"commonPrefixes"` } // ListFiles 用来获取空间文件列表,可以根据需要指定文件的前缀 prefix,文件的目录 delimiter,循环列举的时候下次 // 列举的位置 marker,以及每次返回的文件的最大数量limit,其中limit最大为1000。 func (m *BucketManager) ListFiles(bucket, prefix, delimiter, marker string, limit int) (entries []ListItem, commonPrefixes []string, nextMarker string, hasNext bool, err error) { ret, hNext, e := m.ListFilesWithContext(context.Background(), bucket, ListInputOptionsPrefix(prefix), ListInputOptionsDelimiter(delimiter), ListInputOptionsMarker(marker), ListInputOptionsLimit(limit)) if e != nil { return nil, nil, "", false, e } return ret.Items, ret.CommonPrefixes, ret.Marker, hNext, nil } type listInputOptions struct { prefix string delimiter string marker string limit int } type ListInputOption func(options *listInputOptions) func ListInputOptionsPrefix(prefix string) ListInputOption { return func(input *listInputOptions) { input.prefix = prefix } } func ListInputOptionsDelimiter(delimiter string) ListInputOption { return func(input *listInputOptions) { input.delimiter = delimiter } } func ListInputOptionsMarker(marker string) ListInputOption { return func(input *listInputOptions) { input.marker = marker } } func ListInputOptionsLimit(limit int) ListInputOption { return func(input *listInputOptions) { input.limit = limit } } // ListFilesWithContext // // @Description: 用来获取空间文件列表,可以根据需要指定文件的列举条件 // @receiver m BucketManager // @param ctx context // @param bucket 列举的 bucket // @param options 列举的可选条件 // 列举条件-需要列举 Key 的前缀:ListInputOptionsPrefix(prefix) // 列举条件-文件的目录分隔符:ListInputOptionsDelimiter(delimiter) // 列举条件-下次列举的位置:ListInputOptionsMarker(marker) // 列举条件-每次返回的文件的最大数量:ListInputOptionsLimit(limit) 范围:1~1000 // @return ret 列举的对象数据 // @return hasNext 是否还有数据未被列举 // @return err 列举时的错误信息 func (m *BucketManager) ListFilesWithContext(ctx context.Context, bucket string, options ...ListInputOption) (ret *ListFilesRet, hasNext bool, err error) { if len(bucket) == 0 { return nil, false, errors.New("bucket can't empty") } inputOptions := listInputOptions{} for _, option := range options { option(&inputOptions) } if inputOptions.limit <= 0 || inputOptions.limit > 1000 { return nil, false, errors.New("invalid list limit, only allow [1, 1000]") } ctx = auth.WithCredentialsType(ctx, m.Mac, auth.TokenQiniu) host, reqErr := m.RsfReqHost(bucket) if reqErr != nil { return nil, false, reqErr } ret = &ListFilesRet{} reqURL := fmt.Sprintf("%s%s", host, uriListFiles(bucket, inputOptions.prefix, inputOptions.delimiter, inputOptions.marker, inputOptions.limit)) err = m.Client.CredentialedCall(ctx, m.Mac, auth.TokenQiniu, ret, "POST", reqURL, nil) if err != nil { return nil, false, err } return ret, len(ret.Marker) > 0, nil } type listFilesRet2 struct { Marker string `json:"marker"` Item ListItem `json:"item"` Dir string `json:"dir"` } // ListBucket 用来获取空间文件列表,可以根据需要指定文件的前缀 prefix,文件的目录 delimiter,流式返回每条数据。 // Deprecated func (m *BucketManager) ListBucket(bucket, prefix, delimiter, marker string) (retCh chan listFilesRet2, err error) { return m.ListBucketContext(context.Background(), bucket, prefix, delimiter, marker) } // ListBucketContext 用来获取空间文件列表,可以根据需要指定文件的前缀 prefix,文件的目录 delimiter,流式返回每条数据。 // 接受的context可以用来取消列举操作 // Deprecated func (m *BucketManager) ListBucketContext(ctx context.Context, bucket, prefix, delimiter, marker string) (retCh chan listFilesRet2, err error) { ret, _, lErr := m.ListFilesWithContext(ctx, bucket, ListInputOptionsLimit(250), ListInputOptionsPrefix(prefix), ListInputOptionsDelimiter(delimiter), ListInputOptionsMarker(marker)) if lErr != nil { return nil, lErr } count := len(ret.CommonPrefixes) + len(ret.Items) retCh = make(chan listFilesRet2, count) defer close(retCh) if len(ret.CommonPrefixes) > 0 { for _, commonPrefix := range ret.CommonPrefixes { retCh <- listFilesRet2{ Marker: ret.Marker, Item: ListItem{}, Dir: commonPrefix, } } } if len(ret.Items) > 0 { for _, item := range ret.Items { retCh <- listFilesRet2{ Marker: ret.Marker, Item: item, Dir: "", } } } return retCh, err } func uriListFiles(bucket, prefix, delimiter, marker string, limit int) string { query := make(url.Values) query.Add("bucket", bucket) if prefix != "" { query.Add("prefix", prefix) } if delimiter != "" { query.Add("delimiter", delimiter) } if marker != "" { query.Add("marker", marker) } if limit > 0 { query.Add("limit", strconv.FormatInt(int64(limit), 10)) } return fmt.Sprintf("/list?%s", query.Encode()) }