api-list.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. /*
  2. * MinIO Go Library for Amazon S3 Compatible Cloud Storage
  3. * Copyright 2015-2020 MinIO, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package minio
  18. import (
  19. "context"
  20. "fmt"
  21. "net/http"
  22. "net/url"
  23. "github.com/minio/minio-go/v7/pkg/s3utils"
  24. )
  25. // ListBuckets list all buckets owned by this authenticated user.
  26. //
  27. // This call requires explicit authentication, no anonymous requests are
  28. // allowed for listing buckets.
  29. //
  30. // api := client.New(....)
  31. // for message := range api.ListBuckets(context.Background()) {
  32. // fmt.Println(message)
  33. // }
  34. //
  35. func (c Client) ListBuckets(ctx context.Context) ([]BucketInfo, error) {
  36. // Execute GET on service.
  37. resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{contentSHA256Hex: emptySHA256Hex})
  38. defer closeResponse(resp)
  39. if err != nil {
  40. return nil, err
  41. }
  42. if resp != nil {
  43. if resp.StatusCode != http.StatusOK {
  44. return nil, httpRespToErrorResponse(resp, "", "")
  45. }
  46. }
  47. listAllMyBucketsResult := listAllMyBucketsResult{}
  48. err = xmlDecoder(resp.Body, &listAllMyBucketsResult)
  49. if err != nil {
  50. return nil, err
  51. }
  52. return listAllMyBucketsResult.Buckets.Bucket, nil
  53. }
  54. /// Bucket List Operations.
  55. func (c Client) listObjectsV2(ctx context.Context, bucketName string, opts ListObjectsOptions) <-chan ObjectInfo {
  56. // Allocate new list objects channel.
  57. objectStatCh := make(chan ObjectInfo, 1)
  58. // Default listing is delimited at "/"
  59. delimiter := "/"
  60. if opts.Recursive {
  61. // If recursive we do not delimit.
  62. delimiter = ""
  63. }
  64. // Return object owner information by default
  65. fetchOwner := true
  66. // Validate bucket name.
  67. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  68. defer close(objectStatCh)
  69. objectStatCh <- ObjectInfo{
  70. Err: err,
  71. }
  72. return objectStatCh
  73. }
  74. // Validate incoming object prefix.
  75. if err := s3utils.CheckValidObjectNamePrefix(opts.Prefix); err != nil {
  76. defer close(objectStatCh)
  77. objectStatCh <- ObjectInfo{
  78. Err: err,
  79. }
  80. return objectStatCh
  81. }
  82. // Initiate list objects goroutine here.
  83. go func(objectStatCh chan<- ObjectInfo) {
  84. defer close(objectStatCh)
  85. // Save continuationToken for next request.
  86. var continuationToken string
  87. for {
  88. // Get list of objects a maximum of 1000 per request.
  89. result, err := c.listObjectsV2Query(ctx, bucketName, opts.Prefix, continuationToken,
  90. fetchOwner, opts.WithMetadata, delimiter, opts.StartAfter, opts.MaxKeys, opts.headers)
  91. if err != nil {
  92. objectStatCh <- ObjectInfo{
  93. Err: err,
  94. }
  95. return
  96. }
  97. // If contents are available loop through and send over channel.
  98. for _, object := range result.Contents {
  99. object.ETag = trimEtag(object.ETag)
  100. select {
  101. // Send object content.
  102. case objectStatCh <- object:
  103. // If receives done from the caller, return here.
  104. case <-ctx.Done():
  105. return
  106. }
  107. }
  108. // Send all common prefixes if any.
  109. // NOTE: prefixes are only present if the request is delimited.
  110. for _, obj := range result.CommonPrefixes {
  111. select {
  112. // Send object prefixes.
  113. case objectStatCh <- ObjectInfo{Key: obj.Prefix}:
  114. // If receives done from the caller, return here.
  115. case <-ctx.Done():
  116. return
  117. }
  118. }
  119. // If continuation token present, save it for next request.
  120. if result.NextContinuationToken != "" {
  121. continuationToken = result.NextContinuationToken
  122. }
  123. // Listing ends result is not truncated, return right here.
  124. if !result.IsTruncated {
  125. return
  126. }
  127. }
  128. }(objectStatCh)
  129. return objectStatCh
  130. }
  131. // listObjectsV2Query - (List Objects V2) - List some or all (up to 1000) of the objects in a bucket.
  132. //
  133. // You can use the request parameters as selection criteria to return a subset of the objects in a bucket.
  134. // request parameters :-
  135. // ---------
  136. // ?prefix - Limits the response to keys that begin with the specified prefix.
  137. // ?continuation-token - Used to continue iterating over a set of objects
  138. // ?metadata - Specifies if we want metadata for the objects as part of list operation.
  139. // ?delimiter - A delimiter is a character you use to group keys.
  140. // ?start-after - Sets a marker to start listing lexically at this key onwards.
  141. // ?max-keys - Sets the maximum number of keys returned in the response body.
  142. func (c Client) listObjectsV2Query(ctx context.Context, bucketName, objectPrefix, continuationToken string, fetchOwner, metadata bool, delimiter string, startAfter string, maxkeys int, headers http.Header) (ListBucketV2Result, error) {
  143. // Validate bucket name.
  144. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  145. return ListBucketV2Result{}, err
  146. }
  147. // Validate object prefix.
  148. if err := s3utils.CheckValidObjectNamePrefix(objectPrefix); err != nil {
  149. return ListBucketV2Result{}, err
  150. }
  151. // Get resources properly escaped and lined up before
  152. // using them in http request.
  153. urlValues := make(url.Values)
  154. // Always set list-type in ListObjects V2
  155. urlValues.Set("list-type", "2")
  156. if metadata {
  157. urlValues.Set("metadata", "true")
  158. }
  159. // Set this conditionally if asked
  160. if startAfter != "" {
  161. urlValues.Set("start-after", startAfter)
  162. }
  163. // Always set encoding-type in ListObjects V2
  164. urlValues.Set("encoding-type", "url")
  165. // Set object prefix, prefix value to be set to empty is okay.
  166. urlValues.Set("prefix", objectPrefix)
  167. // Set delimiter, delimiter value to be set to empty is okay.
  168. urlValues.Set("delimiter", delimiter)
  169. // Set continuation token
  170. if continuationToken != "" {
  171. urlValues.Set("continuation-token", continuationToken)
  172. }
  173. // Fetch owner when listing
  174. if fetchOwner {
  175. urlValues.Set("fetch-owner", "true")
  176. }
  177. // Set max keys.
  178. if maxkeys > 0 {
  179. urlValues.Set("max-keys", fmt.Sprintf("%d", maxkeys))
  180. }
  181. // Execute GET on bucket to list objects.
  182. resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{
  183. bucketName: bucketName,
  184. queryValues: urlValues,
  185. contentSHA256Hex: emptySHA256Hex,
  186. customHeader: headers,
  187. })
  188. defer closeResponse(resp)
  189. if err != nil {
  190. return ListBucketV2Result{}, err
  191. }
  192. if resp != nil {
  193. if resp.StatusCode != http.StatusOK {
  194. return ListBucketV2Result{}, httpRespToErrorResponse(resp, bucketName, "")
  195. }
  196. }
  197. // Decode listBuckets XML.
  198. listBucketResult := ListBucketV2Result{}
  199. if err = xmlDecoder(resp.Body, &listBucketResult); err != nil {
  200. return listBucketResult, err
  201. }
  202. // This is an additional verification check to make
  203. // sure proper responses are received.
  204. if listBucketResult.IsTruncated && listBucketResult.NextContinuationToken == "" {
  205. return listBucketResult, ErrorResponse{
  206. Code: "NotImplemented",
  207. Message: "Truncated response should have continuation token set",
  208. }
  209. }
  210. for i, obj := range listBucketResult.Contents {
  211. listBucketResult.Contents[i].Key, err = decodeS3Name(obj.Key, listBucketResult.EncodingType)
  212. if err != nil {
  213. return listBucketResult, err
  214. }
  215. }
  216. for i, obj := range listBucketResult.CommonPrefixes {
  217. listBucketResult.CommonPrefixes[i].Prefix, err = decodeS3Name(obj.Prefix, listBucketResult.EncodingType)
  218. if err != nil {
  219. return listBucketResult, err
  220. }
  221. }
  222. // Success.
  223. return listBucketResult, nil
  224. }
  225. func (c Client) listObjects(ctx context.Context, bucketName string, opts ListObjectsOptions) <-chan ObjectInfo {
  226. // Allocate new list objects channel.
  227. objectStatCh := make(chan ObjectInfo, 1)
  228. // Default listing is delimited at "/"
  229. delimiter := "/"
  230. if opts.Recursive {
  231. // If recursive we do not delimit.
  232. delimiter = ""
  233. }
  234. // Validate bucket name.
  235. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  236. defer close(objectStatCh)
  237. objectStatCh <- ObjectInfo{
  238. Err: err,
  239. }
  240. return objectStatCh
  241. }
  242. // Validate incoming object prefix.
  243. if err := s3utils.CheckValidObjectNamePrefix(opts.Prefix); err != nil {
  244. defer close(objectStatCh)
  245. objectStatCh <- ObjectInfo{
  246. Err: err,
  247. }
  248. return objectStatCh
  249. }
  250. // Initiate list objects goroutine here.
  251. go func(objectStatCh chan<- ObjectInfo) {
  252. defer close(objectStatCh)
  253. marker := opts.StartAfter
  254. for {
  255. // Get list of objects a maximum of 1000 per request.
  256. result, err := c.listObjectsQuery(ctx, bucketName, opts.Prefix, marker, delimiter, opts.MaxKeys, opts.headers)
  257. if err != nil {
  258. objectStatCh <- ObjectInfo{
  259. Err: err,
  260. }
  261. return
  262. }
  263. // If contents are available loop through and send over channel.
  264. for _, object := range result.Contents {
  265. // Save the marker.
  266. marker = object.Key
  267. select {
  268. // Send object content.
  269. case objectStatCh <- object:
  270. // If receives done from the caller, return here.
  271. case <-ctx.Done():
  272. return
  273. }
  274. }
  275. // Send all common prefixes if any.
  276. // NOTE: prefixes are only present if the request is delimited.
  277. for _, obj := range result.CommonPrefixes {
  278. select {
  279. // Send object prefixes.
  280. case objectStatCh <- ObjectInfo{Key: obj.Prefix}:
  281. // If receives done from the caller, return here.
  282. case <-ctx.Done():
  283. return
  284. }
  285. }
  286. // If next marker present, save it for next request.
  287. if result.NextMarker != "" {
  288. marker = result.NextMarker
  289. }
  290. // Listing ends result is not truncated, return right here.
  291. if !result.IsTruncated {
  292. return
  293. }
  294. }
  295. }(objectStatCh)
  296. return objectStatCh
  297. }
  298. func (c Client) listObjectVersions(ctx context.Context, bucketName string, opts ListObjectsOptions) <-chan ObjectInfo {
  299. // Allocate new list objects channel.
  300. resultCh := make(chan ObjectInfo, 1)
  301. // Default listing is delimited at "/"
  302. delimiter := "/"
  303. if opts.Recursive {
  304. // If recursive we do not delimit.
  305. delimiter = ""
  306. }
  307. // Validate bucket name.
  308. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  309. defer close(resultCh)
  310. resultCh <- ObjectInfo{
  311. Err: err,
  312. }
  313. return resultCh
  314. }
  315. // Validate incoming object prefix.
  316. if err := s3utils.CheckValidObjectNamePrefix(opts.Prefix); err != nil {
  317. defer close(resultCh)
  318. resultCh <- ObjectInfo{
  319. Err: err,
  320. }
  321. return resultCh
  322. }
  323. // Initiate list objects goroutine here.
  324. go func(resultCh chan<- ObjectInfo) {
  325. defer close(resultCh)
  326. var (
  327. keyMarker = ""
  328. versionIDMarker = ""
  329. )
  330. for {
  331. // Get list of objects a maximum of 1000 per request.
  332. result, err := c.listObjectVersionsQuery(ctx, bucketName, opts.Prefix, keyMarker, versionIDMarker, delimiter, opts.MaxKeys, opts.headers)
  333. if err != nil {
  334. resultCh <- ObjectInfo{
  335. Err: err,
  336. }
  337. return
  338. }
  339. // If contents are available loop through and send over channel.
  340. for _, version := range result.Versions {
  341. info := ObjectInfo{
  342. ETag: trimEtag(version.ETag),
  343. Key: version.Key,
  344. LastModified: version.LastModified,
  345. Size: version.Size,
  346. Owner: version.Owner,
  347. StorageClass: version.StorageClass,
  348. IsLatest: version.IsLatest,
  349. VersionID: version.VersionID,
  350. IsDeleteMarker: version.isDeleteMarker,
  351. }
  352. select {
  353. // Send object version info.
  354. case resultCh <- info:
  355. // If receives done from the caller, return here.
  356. case <-ctx.Done():
  357. return
  358. }
  359. }
  360. // Send all common prefixes if any.
  361. // NOTE: prefixes are only present if the request is delimited.
  362. for _, obj := range result.CommonPrefixes {
  363. select {
  364. // Send object prefixes.
  365. case resultCh <- ObjectInfo{Key: obj.Prefix}:
  366. // If receives done from the caller, return here.
  367. case <-ctx.Done():
  368. return
  369. }
  370. }
  371. // If next key marker is present, save it for next request.
  372. if result.NextKeyMarker != "" {
  373. keyMarker = result.NextKeyMarker
  374. }
  375. // If next version id marker is present, save it for next request.
  376. if result.NextVersionIDMarker != "" {
  377. versionIDMarker = result.NextVersionIDMarker
  378. }
  379. // Listing ends result is not truncated, return right here.
  380. if !result.IsTruncated {
  381. return
  382. }
  383. }
  384. }(resultCh)
  385. return resultCh
  386. }
  387. // listObjectVersions - (List Object Versions) - List some or all (up to 1000) of the existing objects
  388. // and their versions in a bucket.
  389. //
  390. // You can use the request parameters as selection criteria to return a subset of the objects in a bucket.
  391. // request parameters :-
  392. // ---------
  393. // ?key-marker - Specifies the key to start with when listing objects in a bucket.
  394. // ?version-id-marker - Specifies the version id marker to start with when listing objects with versions in a bucket.
  395. // ?delimiter - A delimiter is a character you use to group keys.
  396. // ?prefix - Limits the response to keys that begin with the specified prefix.
  397. // ?max-keys - Sets the maximum number of keys returned in the response body.
  398. func (c Client) listObjectVersionsQuery(ctx context.Context, bucketName, prefix, keyMarker, versionIDMarker, delimiter string, maxkeys int, headers http.Header) (ListVersionsResult, error) {
  399. // Validate bucket name.
  400. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  401. return ListVersionsResult{}, err
  402. }
  403. // Validate object prefix.
  404. if err := s3utils.CheckValidObjectNamePrefix(prefix); err != nil {
  405. return ListVersionsResult{}, err
  406. }
  407. // Get resources properly escaped and lined up before
  408. // using them in http request.
  409. urlValues := make(url.Values)
  410. // Set versions to trigger versioning API
  411. urlValues.Set("versions", "")
  412. // Set object prefix, prefix value to be set to empty is okay.
  413. urlValues.Set("prefix", prefix)
  414. // Set delimiter, delimiter value to be set to empty is okay.
  415. urlValues.Set("delimiter", delimiter)
  416. // Set object marker.
  417. if keyMarker != "" {
  418. urlValues.Set("key-marker", keyMarker)
  419. }
  420. // Set max keys.
  421. if maxkeys > 0 {
  422. urlValues.Set("max-keys", fmt.Sprintf("%d", maxkeys))
  423. }
  424. // Set version ID marker
  425. if versionIDMarker != "" {
  426. urlValues.Set("version-id-marker", versionIDMarker)
  427. }
  428. // Always set encoding-type
  429. urlValues.Set("encoding-type", "url")
  430. // Execute GET on bucket to list objects.
  431. resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{
  432. bucketName: bucketName,
  433. queryValues: urlValues,
  434. contentSHA256Hex: emptySHA256Hex,
  435. customHeader: headers,
  436. })
  437. defer closeResponse(resp)
  438. if err != nil {
  439. return ListVersionsResult{}, err
  440. }
  441. if resp != nil {
  442. if resp.StatusCode != http.StatusOK {
  443. return ListVersionsResult{}, httpRespToErrorResponse(resp, bucketName, "")
  444. }
  445. }
  446. // Decode ListVersionsResult XML.
  447. listObjectVersionsOutput := ListVersionsResult{}
  448. err = xmlDecoder(resp.Body, &listObjectVersionsOutput)
  449. if err != nil {
  450. return ListVersionsResult{}, err
  451. }
  452. for i, obj := range listObjectVersionsOutput.Versions {
  453. listObjectVersionsOutput.Versions[i].Key, err = decodeS3Name(obj.Key, listObjectVersionsOutput.EncodingType)
  454. if err != nil {
  455. return listObjectVersionsOutput, err
  456. }
  457. }
  458. for i, obj := range listObjectVersionsOutput.CommonPrefixes {
  459. listObjectVersionsOutput.CommonPrefixes[i].Prefix, err = decodeS3Name(obj.Prefix, listObjectVersionsOutput.EncodingType)
  460. if err != nil {
  461. return listObjectVersionsOutput, err
  462. }
  463. }
  464. if listObjectVersionsOutput.NextKeyMarker != "" {
  465. listObjectVersionsOutput.NextKeyMarker, err = decodeS3Name(listObjectVersionsOutput.NextKeyMarker, listObjectVersionsOutput.EncodingType)
  466. if err != nil {
  467. return listObjectVersionsOutput, err
  468. }
  469. }
  470. return listObjectVersionsOutput, nil
  471. }
  472. // listObjects - (List Objects) - List some or all (up to 1000) of the objects in a bucket.
  473. //
  474. // You can use the request parameters as selection criteria to return a subset of the objects in a bucket.
  475. // request parameters :-
  476. // ---------
  477. // ?marker - Specifies the key to start with when listing objects in a bucket.
  478. // ?delimiter - A delimiter is a character you use to group keys.
  479. // ?prefix - Limits the response to keys that begin with the specified prefix.
  480. // ?max-keys - Sets the maximum number of keys returned in the response body.
  481. func (c Client) listObjectsQuery(ctx context.Context, bucketName, objectPrefix, objectMarker, delimiter string, maxkeys int, headers http.Header) (ListBucketResult, error) {
  482. // Validate bucket name.
  483. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  484. return ListBucketResult{}, err
  485. }
  486. // Validate object prefix.
  487. if err := s3utils.CheckValidObjectNamePrefix(objectPrefix); err != nil {
  488. return ListBucketResult{}, err
  489. }
  490. // Get resources properly escaped and lined up before
  491. // using them in http request.
  492. urlValues := make(url.Values)
  493. // Set object prefix, prefix value to be set to empty is okay.
  494. urlValues.Set("prefix", objectPrefix)
  495. // Set delimiter, delimiter value to be set to empty is okay.
  496. urlValues.Set("delimiter", delimiter)
  497. // Set object marker.
  498. if objectMarker != "" {
  499. urlValues.Set("marker", objectMarker)
  500. }
  501. // Set max keys.
  502. if maxkeys > 0 {
  503. urlValues.Set("max-keys", fmt.Sprintf("%d", maxkeys))
  504. }
  505. // Always set encoding-type
  506. urlValues.Set("encoding-type", "url")
  507. // Execute GET on bucket to list objects.
  508. resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{
  509. bucketName: bucketName,
  510. queryValues: urlValues,
  511. contentSHA256Hex: emptySHA256Hex,
  512. customHeader: headers,
  513. })
  514. defer closeResponse(resp)
  515. if err != nil {
  516. return ListBucketResult{}, err
  517. }
  518. if resp != nil {
  519. if resp.StatusCode != http.StatusOK {
  520. return ListBucketResult{}, httpRespToErrorResponse(resp, bucketName, "")
  521. }
  522. }
  523. // Decode listBuckets XML.
  524. listBucketResult := ListBucketResult{}
  525. err = xmlDecoder(resp.Body, &listBucketResult)
  526. if err != nil {
  527. return listBucketResult, err
  528. }
  529. for i, obj := range listBucketResult.Contents {
  530. listBucketResult.Contents[i].Key, err = decodeS3Name(obj.Key, listBucketResult.EncodingType)
  531. if err != nil {
  532. return listBucketResult, err
  533. }
  534. }
  535. for i, obj := range listBucketResult.CommonPrefixes {
  536. listBucketResult.CommonPrefixes[i].Prefix, err = decodeS3Name(obj.Prefix, listBucketResult.EncodingType)
  537. if err != nil {
  538. return listBucketResult, err
  539. }
  540. }
  541. if listBucketResult.NextMarker != "" {
  542. listBucketResult.NextMarker, err = decodeS3Name(listBucketResult.NextMarker, listBucketResult.EncodingType)
  543. if err != nil {
  544. return listBucketResult, err
  545. }
  546. }
  547. return listBucketResult, nil
  548. }
  549. // ListObjectsOptions holds all options of a list object request
  550. type ListObjectsOptions struct {
  551. // Include objects versions in the listing
  552. WithVersions bool
  553. // Include objects metadata in the listing
  554. WithMetadata bool
  555. // Only list objects with the prefix
  556. Prefix string
  557. // Ignore '/' delimiter
  558. Recursive bool
  559. // The maximum number of objects requested per
  560. // batch, advanced use-case not useful for most
  561. // applications
  562. MaxKeys int
  563. // StartAfter start listing lexically at this
  564. // object onwards, this value can also be set
  565. // for Marker when `UseV1` is set to true.
  566. StartAfter string
  567. // Use the deprecated list objects V1 API
  568. UseV1 bool
  569. headers http.Header
  570. }
  571. // Set adds a key value pair to the options. The
  572. // key-value pair will be part of the HTTP GET request
  573. // headers.
  574. func (o *ListObjectsOptions) Set(key, value string) {
  575. if o.headers == nil {
  576. o.headers = make(http.Header)
  577. }
  578. o.headers.Set(key, value)
  579. }
  580. // ListObjects returns objects list after evaluating the passed options.
  581. //
  582. // api := client.New(....)
  583. // for object := range api.ListObjects(ctx, "mytestbucket", minio.ListObjectsOptions{Prefix: "starthere", Recursive:true}) {
  584. // fmt.Println(object)
  585. // }
  586. //
  587. func (c Client) ListObjects(ctx context.Context, bucketName string, opts ListObjectsOptions) <-chan ObjectInfo {
  588. if opts.WithVersions {
  589. return c.listObjectVersions(ctx, bucketName, opts)
  590. }
  591. // Use legacy list objects v1 API
  592. if opts.UseV1 {
  593. return c.listObjects(ctx, bucketName, opts)
  594. }
  595. // Check whether this is snowball region, if yes ListObjectsV2 doesn't work, fallback to listObjectsV1.
  596. if location, ok := c.bucketLocCache.Get(bucketName); ok {
  597. if location == "snowball" {
  598. return c.listObjects(ctx, bucketName, opts)
  599. }
  600. }
  601. return c.listObjectsV2(ctx, bucketName, opts)
  602. }
  603. // ListIncompleteUploads - List incompletely uploaded multipart objects.
  604. //
  605. // ListIncompleteUploads lists all incompleted objects matching the
  606. // objectPrefix from the specified bucket. If recursion is enabled
  607. // it would list all subdirectories and all its contents.
  608. //
  609. // Your input parameters are just bucketName, objectPrefix, recursive.
  610. // If you enable recursive as 'true' this function will return back all
  611. // the multipart objects in a given bucket name.
  612. //
  613. // api := client.New(....)
  614. // // Recurively list all objects in 'mytestbucket'
  615. // recursive := true
  616. // for message := range api.ListIncompleteUploads(context.Background(), "mytestbucket", "starthere", recursive) {
  617. // fmt.Println(message)
  618. // }
  619. func (c Client) ListIncompleteUploads(ctx context.Context, bucketName, objectPrefix string, recursive bool) <-chan ObjectMultipartInfo {
  620. return c.listIncompleteUploads(ctx, bucketName, objectPrefix, recursive)
  621. }
  622. // listIncompleteUploads lists all incomplete uploads.
  623. func (c Client) listIncompleteUploads(ctx context.Context, bucketName, objectPrefix string, recursive bool) <-chan ObjectMultipartInfo {
  624. // Allocate channel for multipart uploads.
  625. objectMultipartStatCh := make(chan ObjectMultipartInfo, 1)
  626. // Delimiter is set to "/" by default.
  627. delimiter := "/"
  628. if recursive {
  629. // If recursive do not delimit.
  630. delimiter = ""
  631. }
  632. // Validate bucket name.
  633. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  634. defer close(objectMultipartStatCh)
  635. objectMultipartStatCh <- ObjectMultipartInfo{
  636. Err: err,
  637. }
  638. return objectMultipartStatCh
  639. }
  640. // Validate incoming object prefix.
  641. if err := s3utils.CheckValidObjectNamePrefix(objectPrefix); err != nil {
  642. defer close(objectMultipartStatCh)
  643. objectMultipartStatCh <- ObjectMultipartInfo{
  644. Err: err,
  645. }
  646. return objectMultipartStatCh
  647. }
  648. go func(objectMultipartStatCh chan<- ObjectMultipartInfo) {
  649. defer close(objectMultipartStatCh)
  650. // object and upload ID marker for future requests.
  651. var objectMarker string
  652. var uploadIDMarker string
  653. for {
  654. // list all multipart uploads.
  655. result, err := c.listMultipartUploadsQuery(ctx, bucketName, objectMarker, uploadIDMarker, objectPrefix, delimiter, 0)
  656. if err != nil {
  657. objectMultipartStatCh <- ObjectMultipartInfo{
  658. Err: err,
  659. }
  660. return
  661. }
  662. objectMarker = result.NextKeyMarker
  663. uploadIDMarker = result.NextUploadIDMarker
  664. // Send all multipart uploads.
  665. for _, obj := range result.Uploads {
  666. // Calculate total size of the uploaded parts if 'aggregateSize' is enabled.
  667. select {
  668. // Send individual uploads here.
  669. case objectMultipartStatCh <- obj:
  670. // If the context is canceled
  671. case <-ctx.Done():
  672. return
  673. }
  674. }
  675. // Send all common prefixes if any.
  676. // NOTE: prefixes are only present if the request is delimited.
  677. for _, obj := range result.CommonPrefixes {
  678. select {
  679. // Send delimited prefixes here.
  680. case objectMultipartStatCh <- ObjectMultipartInfo{Key: obj.Prefix, Size: 0}:
  681. // If context is canceled.
  682. case <-ctx.Done():
  683. return
  684. }
  685. }
  686. // Listing ends if result not truncated, return right here.
  687. if !result.IsTruncated {
  688. return
  689. }
  690. }
  691. }(objectMultipartStatCh)
  692. // return.
  693. return objectMultipartStatCh
  694. }
  695. // listMultipartUploadsQuery - (List Multipart Uploads).
  696. // - Lists some or all (up to 1000) in-progress multipart uploads in a bucket.
  697. //
  698. // You can use the request parameters as selection criteria to return a subset of the uploads in a bucket.
  699. // request parameters. :-
  700. // ---------
  701. // ?key-marker - Specifies the multipart upload after which listing should begin.
  702. // ?upload-id-marker - Together with key-marker specifies the multipart upload after which listing should begin.
  703. // ?delimiter - A delimiter is a character you use to group keys.
  704. // ?prefix - Limits the response to keys that begin with the specified prefix.
  705. // ?max-uploads - Sets the maximum number of multipart uploads returned in the response body.
  706. func (c Client) listMultipartUploadsQuery(ctx context.Context, bucketName, keyMarker, uploadIDMarker, prefix, delimiter string, maxUploads int) (ListMultipartUploadsResult, error) {
  707. // Get resources properly escaped and lined up before using them in http request.
  708. urlValues := make(url.Values)
  709. // Set uploads.
  710. urlValues.Set("uploads", "")
  711. // Set object key marker.
  712. if keyMarker != "" {
  713. urlValues.Set("key-marker", keyMarker)
  714. }
  715. // Set upload id marker.
  716. if uploadIDMarker != "" {
  717. urlValues.Set("upload-id-marker", uploadIDMarker)
  718. }
  719. // Set object prefix, prefix value to be set to empty is okay.
  720. urlValues.Set("prefix", prefix)
  721. // Set delimiter, delimiter value to be set to empty is okay.
  722. urlValues.Set("delimiter", delimiter)
  723. // Always set encoding-type
  724. urlValues.Set("encoding-type", "url")
  725. // maxUploads should be 1000 or less.
  726. if maxUploads > 0 {
  727. // Set max-uploads.
  728. urlValues.Set("max-uploads", fmt.Sprintf("%d", maxUploads))
  729. }
  730. // Execute GET on bucketName to list multipart uploads.
  731. resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{
  732. bucketName: bucketName,
  733. queryValues: urlValues,
  734. contentSHA256Hex: emptySHA256Hex,
  735. })
  736. defer closeResponse(resp)
  737. if err != nil {
  738. return ListMultipartUploadsResult{}, err
  739. }
  740. if resp != nil {
  741. if resp.StatusCode != http.StatusOK {
  742. return ListMultipartUploadsResult{}, httpRespToErrorResponse(resp, bucketName, "")
  743. }
  744. }
  745. // Decode response body.
  746. listMultipartUploadsResult := ListMultipartUploadsResult{}
  747. err = xmlDecoder(resp.Body, &listMultipartUploadsResult)
  748. if err != nil {
  749. return listMultipartUploadsResult, err
  750. }
  751. listMultipartUploadsResult.NextKeyMarker, err = decodeS3Name(listMultipartUploadsResult.NextKeyMarker, listMultipartUploadsResult.EncodingType)
  752. if err != nil {
  753. return listMultipartUploadsResult, err
  754. }
  755. listMultipartUploadsResult.NextUploadIDMarker, err = decodeS3Name(listMultipartUploadsResult.NextUploadIDMarker, listMultipartUploadsResult.EncodingType)
  756. if err != nil {
  757. return listMultipartUploadsResult, err
  758. }
  759. for i, obj := range listMultipartUploadsResult.Uploads {
  760. listMultipartUploadsResult.Uploads[i].Key, err = decodeS3Name(obj.Key, listMultipartUploadsResult.EncodingType)
  761. if err != nil {
  762. return listMultipartUploadsResult, err
  763. }
  764. }
  765. for i, obj := range listMultipartUploadsResult.CommonPrefixes {
  766. listMultipartUploadsResult.CommonPrefixes[i].Prefix, err = decodeS3Name(obj.Prefix, listMultipartUploadsResult.EncodingType)
  767. if err != nil {
  768. return listMultipartUploadsResult, err
  769. }
  770. }
  771. return listMultipartUploadsResult, nil
  772. }
  773. // listObjectParts list all object parts recursively.
  774. func (c Client) listObjectParts(ctx context.Context, bucketName, objectName, uploadID string) (partsInfo map[int]ObjectPart, err error) {
  775. // Part number marker for the next batch of request.
  776. var nextPartNumberMarker int
  777. partsInfo = make(map[int]ObjectPart)
  778. for {
  779. // Get list of uploaded parts a maximum of 1000 per request.
  780. listObjPartsResult, err := c.listObjectPartsQuery(ctx, bucketName, objectName, uploadID, nextPartNumberMarker, 1000)
  781. if err != nil {
  782. return nil, err
  783. }
  784. // Append to parts info.
  785. for _, part := range listObjPartsResult.ObjectParts {
  786. // Trim off the odd double quotes from ETag in the beginning and end.
  787. part.ETag = trimEtag(part.ETag)
  788. partsInfo[part.PartNumber] = part
  789. }
  790. // Keep part number marker, for the next iteration.
  791. nextPartNumberMarker = listObjPartsResult.NextPartNumberMarker
  792. // Listing ends result is not truncated, return right here.
  793. if !listObjPartsResult.IsTruncated {
  794. break
  795. }
  796. }
  797. // Return all the parts.
  798. return partsInfo, nil
  799. }
  800. // findUploadIDs lists all incomplete uploads and find the uploadIDs of the matching object name.
  801. func (c Client) findUploadIDs(ctx context.Context, bucketName, objectName string) ([]string, error) {
  802. var uploadIDs []string
  803. // Make list incomplete uploads recursive.
  804. isRecursive := true
  805. // List all incomplete uploads.
  806. for mpUpload := range c.listIncompleteUploads(ctx, bucketName, objectName, isRecursive) {
  807. if mpUpload.Err != nil {
  808. return nil, mpUpload.Err
  809. }
  810. if objectName == mpUpload.Key {
  811. uploadIDs = append(uploadIDs, mpUpload.UploadID)
  812. }
  813. }
  814. // Return the latest upload id.
  815. return uploadIDs, nil
  816. }
  817. // listObjectPartsQuery (List Parts query)
  818. // - lists some or all (up to 1000) parts that have been uploaded
  819. // for a specific multipart upload
  820. //
  821. // You can use the request parameters as selection criteria to return
  822. // a subset of the uploads in a bucket, request parameters :-
  823. // ---------
  824. // ?part-number-marker - Specifies the part after which listing should
  825. // begin.
  826. // ?max-parts - Maximum parts to be listed per request.
  827. func (c Client) listObjectPartsQuery(ctx context.Context, bucketName, objectName, uploadID string, partNumberMarker, maxParts int) (ListObjectPartsResult, error) {
  828. // Get resources properly escaped and lined up before using them in http request.
  829. urlValues := make(url.Values)
  830. // Set part number marker.
  831. urlValues.Set("part-number-marker", fmt.Sprintf("%d", partNumberMarker))
  832. // Set upload id.
  833. urlValues.Set("uploadId", uploadID)
  834. // maxParts should be 1000 or less.
  835. if maxParts > 0 {
  836. // Set max parts.
  837. urlValues.Set("max-parts", fmt.Sprintf("%d", maxParts))
  838. }
  839. // Execute GET on objectName to get list of parts.
  840. resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{
  841. bucketName: bucketName,
  842. objectName: objectName,
  843. queryValues: urlValues,
  844. contentSHA256Hex: emptySHA256Hex,
  845. })
  846. defer closeResponse(resp)
  847. if err != nil {
  848. return ListObjectPartsResult{}, err
  849. }
  850. if resp != nil {
  851. if resp.StatusCode != http.StatusOK {
  852. return ListObjectPartsResult{}, httpRespToErrorResponse(resp, bucketName, objectName)
  853. }
  854. }
  855. // Decode list object parts XML.
  856. listObjectPartsResult := ListObjectPartsResult{}
  857. err = xmlDecoder(resp.Body, &listObjectPartsResult)
  858. if err != nil {
  859. return listObjectPartsResult, err
  860. }
  861. return listObjectPartsResult, nil
  862. }
  863. // Decode an S3 object name according to the encoding type
  864. func decodeS3Name(name, encodingType string) (string, error) {
  865. switch encodingType {
  866. case "url":
  867. return url.QueryUnescape(name)
  868. default:
  869. return name, nil
  870. }
  871. }