api-list.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. /*
  2. * Minio Go Library for Amazon S3 Compatible Cloud Storage
  3. * Copyright 2015-2017 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. "errors"
  21. "fmt"
  22. "net/http"
  23. "net/url"
  24. "strings"
  25. "github.com/minio/minio-go/pkg/s3utils"
  26. )
  27. // ListBuckets list all buckets owned by this authenticated user.
  28. //
  29. // This call requires explicit authentication, no anonymous requests are
  30. // allowed for listing buckets.
  31. //
  32. // api := client.New(....)
  33. // for message := range api.ListBuckets() {
  34. // fmt.Println(message)
  35. // }
  36. //
  37. func (c Client) ListBuckets() ([]BucketInfo, error) {
  38. // Execute GET on service.
  39. resp, err := c.executeMethod(context.Background(), "GET", requestMetadata{contentSHA256Hex: emptySHA256Hex})
  40. defer closeResponse(resp)
  41. if err != nil {
  42. return nil, err
  43. }
  44. if resp != nil {
  45. if resp.StatusCode != http.StatusOK {
  46. return nil, httpRespToErrorResponse(resp, "", "")
  47. }
  48. }
  49. listAllMyBucketsResult := listAllMyBucketsResult{}
  50. err = xmlDecoder(resp.Body, &listAllMyBucketsResult)
  51. if err != nil {
  52. return nil, err
  53. }
  54. return listAllMyBucketsResult.Buckets.Bucket, nil
  55. }
  56. /// Bucket Read Operations.
  57. // ListObjectsV2 lists all objects matching the objectPrefix from
  58. // the specified bucket. If recursion is enabled it would list
  59. // all subdirectories and all its contents.
  60. //
  61. // Your input parameters are just bucketName, objectPrefix, recursive
  62. // and a done channel for pro-actively closing the internal go
  63. // routine. If you enable recursive as 'true' this function will
  64. // return back all the objects in a given bucket name and object
  65. // prefix.
  66. //
  67. // api := client.New(....)
  68. // // Create a done channel.
  69. // doneCh := make(chan struct{})
  70. // defer close(doneCh)
  71. // // Recursively list all objects in 'mytestbucket'
  72. // recursive := true
  73. // for message := range api.ListObjectsV2("mytestbucket", "starthere", recursive, doneCh) {
  74. // fmt.Println(message)
  75. // }
  76. //
  77. func (c Client) ListObjectsV2(bucketName, objectPrefix string, recursive bool, doneCh <-chan struct{}) <-chan ObjectInfo {
  78. // Allocate new list objects channel.
  79. objectStatCh := make(chan ObjectInfo, 1)
  80. // Default listing is delimited at "/"
  81. delimiter := "/"
  82. if recursive {
  83. // If recursive we do not delimit.
  84. delimiter = ""
  85. }
  86. // Return object owner information by default
  87. fetchOwner := true
  88. // Validate bucket name.
  89. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  90. defer close(objectStatCh)
  91. objectStatCh <- ObjectInfo{
  92. Err: err,
  93. }
  94. return objectStatCh
  95. }
  96. // Validate incoming object prefix.
  97. if err := s3utils.CheckValidObjectNamePrefix(objectPrefix); err != nil {
  98. defer close(objectStatCh)
  99. objectStatCh <- ObjectInfo{
  100. Err: err,
  101. }
  102. return objectStatCh
  103. }
  104. // Initiate list objects goroutine here.
  105. go func(objectStatCh chan<- ObjectInfo) {
  106. defer close(objectStatCh)
  107. // Save continuationToken for next request.
  108. var continuationToken string
  109. for {
  110. // Get list of objects a maximum of 1000 per request.
  111. result, err := c.listObjectsV2Query(bucketName, objectPrefix, continuationToken, fetchOwner, delimiter, 1000, "")
  112. if err != nil {
  113. objectStatCh <- ObjectInfo{
  114. Err: err,
  115. }
  116. return
  117. }
  118. // If contents are available loop through and send over channel.
  119. for _, object := range result.Contents {
  120. select {
  121. // Send object content.
  122. case objectStatCh <- object:
  123. // If receives done from the caller, return here.
  124. case <-doneCh:
  125. return
  126. }
  127. }
  128. // Send all common prefixes if any.
  129. // NOTE: prefixes are only present if the request is delimited.
  130. for _, obj := range result.CommonPrefixes {
  131. select {
  132. // Send object prefixes.
  133. case objectStatCh <- ObjectInfo{
  134. Key: obj.Prefix,
  135. Size: 0,
  136. }:
  137. // If receives done from the caller, return here.
  138. case <-doneCh:
  139. return
  140. }
  141. }
  142. // If continuation token present, save it for next request.
  143. if result.NextContinuationToken != "" {
  144. continuationToken = result.NextContinuationToken
  145. }
  146. // Listing ends result is not truncated, return right here.
  147. if !result.IsTruncated {
  148. return
  149. }
  150. }
  151. }(objectStatCh)
  152. return objectStatCh
  153. }
  154. // listObjectsV2Query - (List Objects V2) - List some or all (up to 1000) of the objects in a bucket.
  155. //
  156. // You can use the request parameters as selection criteria to return a subset of the objects in a bucket.
  157. // request parameters :-
  158. // ---------
  159. // ?continuation-token - Used to continue iterating over a set of objects
  160. // ?delimiter - A delimiter is a character you use to group keys.
  161. // ?prefix - Limits the response to keys that begin with the specified prefix.
  162. // ?max-keys - Sets the maximum number of keys returned in the response body.
  163. // ?start-after - Specifies the key to start after when listing objects in a bucket.
  164. func (c Client) listObjectsV2Query(bucketName, objectPrefix, continuationToken string, fetchOwner bool, delimiter string, maxkeys int, startAfter string) (ListBucketV2Result, error) {
  165. // Validate bucket name.
  166. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  167. return ListBucketV2Result{}, err
  168. }
  169. // Validate object prefix.
  170. if err := s3utils.CheckValidObjectNamePrefix(objectPrefix); err != nil {
  171. return ListBucketV2Result{}, err
  172. }
  173. // Get resources properly escaped and lined up before
  174. // using them in http request.
  175. urlValues := make(url.Values)
  176. // Always set list-type in ListObjects V2
  177. urlValues.Set("list-type", "2")
  178. // Set object prefix, prefix value to be set to empty is okay.
  179. urlValues.Set("prefix", objectPrefix)
  180. // Set delimiter, delimiter value to be set to empty is okay.
  181. urlValues.Set("delimiter", delimiter)
  182. // Set continuation token
  183. if continuationToken != "" {
  184. urlValues.Set("continuation-token", continuationToken)
  185. }
  186. // Fetch owner when listing
  187. if fetchOwner {
  188. urlValues.Set("fetch-owner", "true")
  189. }
  190. // maxkeys should default to 1000 or less.
  191. if maxkeys == 0 || maxkeys > 1000 {
  192. maxkeys = 1000
  193. }
  194. // Set max keys.
  195. urlValues.Set("max-keys", fmt.Sprintf("%d", maxkeys))
  196. // Set start-after
  197. if startAfter != "" {
  198. urlValues.Set("start-after", startAfter)
  199. }
  200. // Execute GET on bucket to list objects.
  201. resp, err := c.executeMethod(context.Background(), "GET", requestMetadata{
  202. bucketName: bucketName,
  203. queryValues: urlValues,
  204. contentSHA256Hex: emptySHA256Hex,
  205. })
  206. defer closeResponse(resp)
  207. if err != nil {
  208. return ListBucketV2Result{}, err
  209. }
  210. if resp != nil {
  211. if resp.StatusCode != http.StatusOK {
  212. return ListBucketV2Result{}, httpRespToErrorResponse(resp, bucketName, "")
  213. }
  214. }
  215. // Decode listBuckets XML.
  216. listBucketResult := ListBucketV2Result{}
  217. if err = xmlDecoder(resp.Body, &listBucketResult); err != nil {
  218. return listBucketResult, err
  219. }
  220. // This is an additional verification check to make
  221. // sure proper responses are received.
  222. if listBucketResult.IsTruncated && listBucketResult.NextContinuationToken == "" {
  223. return listBucketResult, errors.New("Truncated response should have continuation token set")
  224. }
  225. // Success.
  226. return listBucketResult, nil
  227. }
  228. // ListObjects - (List Objects) - List some objects or all recursively.
  229. //
  230. // ListObjects lists all objects matching the objectPrefix from
  231. // the specified bucket. If recursion is enabled it would list
  232. // all subdirectories and all its contents.
  233. //
  234. // Your input parameters are just bucketName, objectPrefix, recursive
  235. // and a done channel for pro-actively closing the internal go
  236. // routine. If you enable recursive as 'true' this function will
  237. // return back all the objects in a given bucket name and object
  238. // prefix.
  239. //
  240. // api := client.New(....)
  241. // // Create a done channel.
  242. // doneCh := make(chan struct{})
  243. // defer close(doneCh)
  244. // // Recurively list all objects in 'mytestbucket'
  245. // recursive := true
  246. // for message := range api.ListObjects("mytestbucket", "starthere", recursive, doneCh) {
  247. // fmt.Println(message)
  248. // }
  249. //
  250. func (c Client) ListObjects(bucketName, objectPrefix string, recursive bool, doneCh <-chan struct{}) <-chan ObjectInfo {
  251. // Allocate new list objects channel.
  252. objectStatCh := make(chan ObjectInfo, 1)
  253. // Default listing is delimited at "/"
  254. delimiter := "/"
  255. if recursive {
  256. // If recursive we do not delimit.
  257. delimiter = ""
  258. }
  259. // Validate bucket name.
  260. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  261. defer close(objectStatCh)
  262. objectStatCh <- ObjectInfo{
  263. Err: err,
  264. }
  265. return objectStatCh
  266. }
  267. // Validate incoming object prefix.
  268. if err := s3utils.CheckValidObjectNamePrefix(objectPrefix); err != nil {
  269. defer close(objectStatCh)
  270. objectStatCh <- ObjectInfo{
  271. Err: err,
  272. }
  273. return objectStatCh
  274. }
  275. // Initiate list objects goroutine here.
  276. go func(objectStatCh chan<- ObjectInfo) {
  277. defer close(objectStatCh)
  278. // Save marker for next request.
  279. var marker string
  280. for {
  281. // Get list of objects a maximum of 1000 per request.
  282. result, err := c.listObjectsQuery(bucketName, objectPrefix, marker, delimiter, 1000)
  283. if err != nil {
  284. objectStatCh <- ObjectInfo{
  285. Err: err,
  286. }
  287. return
  288. }
  289. // If contents are available loop through and send over channel.
  290. for _, object := range result.Contents {
  291. // Save the marker.
  292. marker = object.Key
  293. select {
  294. // Send object content.
  295. case objectStatCh <- object:
  296. // If receives done from the caller, return here.
  297. case <-doneCh:
  298. return
  299. }
  300. }
  301. // Send all common prefixes if any.
  302. // NOTE: prefixes are only present if the request is delimited.
  303. for _, obj := range result.CommonPrefixes {
  304. object := ObjectInfo{}
  305. object.Key = obj.Prefix
  306. object.Size = 0
  307. select {
  308. // Send object prefixes.
  309. case objectStatCh <- object:
  310. // If receives done from the caller, return here.
  311. case <-doneCh:
  312. return
  313. }
  314. }
  315. // If next marker present, save it for next request.
  316. if result.NextMarker != "" {
  317. marker = result.NextMarker
  318. }
  319. // Listing ends result is not truncated, return right here.
  320. if !result.IsTruncated {
  321. return
  322. }
  323. }
  324. }(objectStatCh)
  325. return objectStatCh
  326. }
  327. // listObjects - (List Objects) - List some or all (up to 1000) of the objects in a bucket.
  328. //
  329. // You can use the request parameters as selection criteria to return a subset of the objects in a bucket.
  330. // request parameters :-
  331. // ---------
  332. // ?marker - Specifies the key to start with when listing objects in a bucket.
  333. // ?delimiter - A delimiter is a character you use to group keys.
  334. // ?prefix - Limits the response to keys that begin with the specified prefix.
  335. // ?max-keys - Sets the maximum number of keys returned in the response body.
  336. func (c Client) listObjectsQuery(bucketName, objectPrefix, objectMarker, delimiter string, maxkeys int) (ListBucketResult, error) {
  337. // Validate bucket name.
  338. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  339. return ListBucketResult{}, err
  340. }
  341. // Validate object prefix.
  342. if err := s3utils.CheckValidObjectNamePrefix(objectPrefix); err != nil {
  343. return ListBucketResult{}, err
  344. }
  345. // Get resources properly escaped and lined up before
  346. // using them in http request.
  347. urlValues := make(url.Values)
  348. // Set object prefix, prefix value to be set to empty is okay.
  349. urlValues.Set("prefix", objectPrefix)
  350. // Set delimiter, delimiter value to be set to empty is okay.
  351. urlValues.Set("delimiter", delimiter)
  352. // Set object marker.
  353. if objectMarker != "" {
  354. urlValues.Set("marker", objectMarker)
  355. }
  356. // maxkeys should default to 1000 or less.
  357. if maxkeys == 0 || maxkeys > 1000 {
  358. maxkeys = 1000
  359. }
  360. // Set max keys.
  361. urlValues.Set("max-keys", fmt.Sprintf("%d", maxkeys))
  362. // Execute GET on bucket to list objects.
  363. resp, err := c.executeMethod(context.Background(), "GET", requestMetadata{
  364. bucketName: bucketName,
  365. queryValues: urlValues,
  366. contentSHA256Hex: emptySHA256Hex,
  367. })
  368. defer closeResponse(resp)
  369. if err != nil {
  370. return ListBucketResult{}, err
  371. }
  372. if resp != nil {
  373. if resp.StatusCode != http.StatusOK {
  374. return ListBucketResult{}, httpRespToErrorResponse(resp, bucketName, "")
  375. }
  376. }
  377. // Decode listBuckets XML.
  378. listBucketResult := ListBucketResult{}
  379. err = xmlDecoder(resp.Body, &listBucketResult)
  380. if err != nil {
  381. return listBucketResult, err
  382. }
  383. return listBucketResult, nil
  384. }
  385. // ListIncompleteUploads - List incompletely uploaded multipart objects.
  386. //
  387. // ListIncompleteUploads lists all incompleted objects matching the
  388. // objectPrefix from the specified bucket. If recursion is enabled
  389. // it would list all subdirectories and all its contents.
  390. //
  391. // Your input parameters are just bucketName, objectPrefix, recursive
  392. // and a done channel to pro-actively close the internal go routine.
  393. // If you enable recursive as 'true' this function will return back all
  394. // the multipart objects in a given bucket name.
  395. //
  396. // api := client.New(....)
  397. // // Create a done channel.
  398. // doneCh := make(chan struct{})
  399. // defer close(doneCh)
  400. // // Recurively list all objects in 'mytestbucket'
  401. // recursive := true
  402. // for message := range api.ListIncompleteUploads("mytestbucket", "starthere", recursive) {
  403. // fmt.Println(message)
  404. // }
  405. //
  406. func (c Client) ListIncompleteUploads(bucketName, objectPrefix string, recursive bool, doneCh <-chan struct{}) <-chan ObjectMultipartInfo {
  407. // Turn on size aggregation of individual parts.
  408. isAggregateSize := true
  409. return c.listIncompleteUploads(bucketName, objectPrefix, recursive, isAggregateSize, doneCh)
  410. }
  411. // listIncompleteUploads lists all incomplete uploads.
  412. func (c Client) listIncompleteUploads(bucketName, objectPrefix string, recursive, aggregateSize bool, doneCh <-chan struct{}) <-chan ObjectMultipartInfo {
  413. // Allocate channel for multipart uploads.
  414. objectMultipartStatCh := make(chan ObjectMultipartInfo, 1)
  415. // Delimiter is set to "/" by default.
  416. delimiter := "/"
  417. if recursive {
  418. // If recursive do not delimit.
  419. delimiter = ""
  420. }
  421. // Validate bucket name.
  422. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  423. defer close(objectMultipartStatCh)
  424. objectMultipartStatCh <- ObjectMultipartInfo{
  425. Err: err,
  426. }
  427. return objectMultipartStatCh
  428. }
  429. // Validate incoming object prefix.
  430. if err := s3utils.CheckValidObjectNamePrefix(objectPrefix); err != nil {
  431. defer close(objectMultipartStatCh)
  432. objectMultipartStatCh <- ObjectMultipartInfo{
  433. Err: err,
  434. }
  435. return objectMultipartStatCh
  436. }
  437. go func(objectMultipartStatCh chan<- ObjectMultipartInfo) {
  438. defer close(objectMultipartStatCh)
  439. // object and upload ID marker for future requests.
  440. var objectMarker string
  441. var uploadIDMarker string
  442. for {
  443. // list all multipart uploads.
  444. result, err := c.listMultipartUploadsQuery(bucketName, objectMarker, uploadIDMarker, objectPrefix, delimiter, 1000)
  445. if err != nil {
  446. objectMultipartStatCh <- ObjectMultipartInfo{
  447. Err: err,
  448. }
  449. return
  450. }
  451. // Save objectMarker and uploadIDMarker for next request.
  452. objectMarker = result.NextKeyMarker
  453. uploadIDMarker = result.NextUploadIDMarker
  454. // Send all multipart uploads.
  455. for _, obj := range result.Uploads {
  456. // Calculate total size of the uploaded parts if 'aggregateSize' is enabled.
  457. if aggregateSize {
  458. // Get total multipart size.
  459. obj.Size, err = c.getTotalMultipartSize(bucketName, obj.Key, obj.UploadID)
  460. if err != nil {
  461. objectMultipartStatCh <- ObjectMultipartInfo{
  462. Err: err,
  463. }
  464. continue
  465. }
  466. }
  467. select {
  468. // Send individual uploads here.
  469. case objectMultipartStatCh <- obj:
  470. // If done channel return here.
  471. case <-doneCh:
  472. return
  473. }
  474. }
  475. // Send all common prefixes if any.
  476. // NOTE: prefixes are only present if the request is delimited.
  477. for _, obj := range result.CommonPrefixes {
  478. object := ObjectMultipartInfo{}
  479. object.Key = obj.Prefix
  480. object.Size = 0
  481. select {
  482. // Send delimited prefixes here.
  483. case objectMultipartStatCh <- object:
  484. // If done channel return here.
  485. case <-doneCh:
  486. return
  487. }
  488. }
  489. // Listing ends if result not truncated, return right here.
  490. if !result.IsTruncated {
  491. return
  492. }
  493. }
  494. }(objectMultipartStatCh)
  495. // return.
  496. return objectMultipartStatCh
  497. }
  498. // listMultipartUploads - (List Multipart Uploads).
  499. // - Lists some or all (up to 1000) in-progress multipart uploads in a bucket.
  500. //
  501. // You can use the request parameters as selection criteria to return a subset of the uploads in a bucket.
  502. // request parameters. :-
  503. // ---------
  504. // ?key-marker - Specifies the multipart upload after which listing should begin.
  505. // ?upload-id-marker - Together with key-marker specifies the multipart upload after which listing should begin.
  506. // ?delimiter - A delimiter is a character you use to group keys.
  507. // ?prefix - Limits the response to keys that begin with the specified prefix.
  508. // ?max-uploads - Sets the maximum number of multipart uploads returned in the response body.
  509. func (c Client) listMultipartUploadsQuery(bucketName, keyMarker, uploadIDMarker, prefix, delimiter string, maxUploads int) (ListMultipartUploadsResult, error) {
  510. // Get resources properly escaped and lined up before using them in http request.
  511. urlValues := make(url.Values)
  512. // Set uploads.
  513. urlValues.Set("uploads", "")
  514. // Set object key marker.
  515. if keyMarker != "" {
  516. urlValues.Set("key-marker", keyMarker)
  517. }
  518. // Set upload id marker.
  519. if uploadIDMarker != "" {
  520. urlValues.Set("upload-id-marker", uploadIDMarker)
  521. }
  522. // Set object prefix, prefix value to be set to empty is okay.
  523. urlValues.Set("prefix", prefix)
  524. // Set delimiter, delimiter value to be set to empty is okay.
  525. urlValues.Set("delimiter", delimiter)
  526. // maxUploads should be 1000 or less.
  527. if maxUploads == 0 || maxUploads > 1000 {
  528. maxUploads = 1000
  529. }
  530. // Set max-uploads.
  531. urlValues.Set("max-uploads", fmt.Sprintf("%d", maxUploads))
  532. // Execute GET on bucketName to list multipart uploads.
  533. resp, err := c.executeMethod(context.Background(), "GET", requestMetadata{
  534. bucketName: bucketName,
  535. queryValues: urlValues,
  536. contentSHA256Hex: emptySHA256Hex,
  537. })
  538. defer closeResponse(resp)
  539. if err != nil {
  540. return ListMultipartUploadsResult{}, err
  541. }
  542. if resp != nil {
  543. if resp.StatusCode != http.StatusOK {
  544. return ListMultipartUploadsResult{}, httpRespToErrorResponse(resp, bucketName, "")
  545. }
  546. }
  547. // Decode response body.
  548. listMultipartUploadsResult := ListMultipartUploadsResult{}
  549. err = xmlDecoder(resp.Body, &listMultipartUploadsResult)
  550. if err != nil {
  551. return listMultipartUploadsResult, err
  552. }
  553. return listMultipartUploadsResult, nil
  554. }
  555. // listObjectParts list all object parts recursively.
  556. func (c Client) listObjectParts(bucketName, objectName, uploadID string) (partsInfo map[int]ObjectPart, err error) {
  557. // Part number marker for the next batch of request.
  558. var nextPartNumberMarker int
  559. partsInfo = make(map[int]ObjectPart)
  560. for {
  561. // Get list of uploaded parts a maximum of 1000 per request.
  562. listObjPartsResult, err := c.listObjectPartsQuery(bucketName, objectName, uploadID, nextPartNumberMarker, 1000)
  563. if err != nil {
  564. return nil, err
  565. }
  566. // Append to parts info.
  567. for _, part := range listObjPartsResult.ObjectParts {
  568. // Trim off the odd double quotes from ETag in the beginning and end.
  569. part.ETag = strings.TrimPrefix(part.ETag, "\"")
  570. part.ETag = strings.TrimSuffix(part.ETag, "\"")
  571. partsInfo[part.PartNumber] = part
  572. }
  573. // Keep part number marker, for the next iteration.
  574. nextPartNumberMarker = listObjPartsResult.NextPartNumberMarker
  575. // Listing ends result is not truncated, return right here.
  576. if !listObjPartsResult.IsTruncated {
  577. break
  578. }
  579. }
  580. // Return all the parts.
  581. return partsInfo, nil
  582. }
  583. // findUploadIDs lists all incomplete uploads and find the uploadIDs of the matching object name.
  584. func (c Client) findUploadIDs(bucketName, objectName string) ([]string, error) {
  585. var uploadIDs []string
  586. // Make list incomplete uploads recursive.
  587. isRecursive := true
  588. // Turn off size aggregation of individual parts, in this request.
  589. isAggregateSize := false
  590. // Create done channel to cleanup the routine.
  591. doneCh := make(chan struct{})
  592. defer close(doneCh)
  593. // List all incomplete uploads.
  594. for mpUpload := range c.listIncompleteUploads(bucketName, objectName, isRecursive, isAggregateSize, doneCh) {
  595. if mpUpload.Err != nil {
  596. return nil, mpUpload.Err
  597. }
  598. if objectName == mpUpload.Key {
  599. uploadIDs = append(uploadIDs, mpUpload.UploadID)
  600. }
  601. }
  602. // Return the latest upload id.
  603. return uploadIDs, nil
  604. }
  605. // getTotalMultipartSize - calculate total uploaded size for the a given multipart object.
  606. func (c Client) getTotalMultipartSize(bucketName, objectName, uploadID string) (size int64, err error) {
  607. // Iterate over all parts and aggregate the size.
  608. partsInfo, err := c.listObjectParts(bucketName, objectName, uploadID)
  609. if err != nil {
  610. return 0, err
  611. }
  612. for _, partInfo := range partsInfo {
  613. size += partInfo.Size
  614. }
  615. return size, nil
  616. }
  617. // listObjectPartsQuery (List Parts query)
  618. // - lists some or all (up to 1000) parts that have been uploaded
  619. // for a specific multipart upload
  620. //
  621. // You can use the request parameters as selection criteria to return
  622. // a subset of the uploads in a bucket, request parameters :-
  623. // ---------
  624. // ?part-number-marker - Specifies the part after which listing should
  625. // begin.
  626. // ?max-parts - Maximum parts to be listed per request.
  627. func (c Client) listObjectPartsQuery(bucketName, objectName, uploadID string, partNumberMarker, maxParts int) (ListObjectPartsResult, error) {
  628. // Get resources properly escaped and lined up before using them in http request.
  629. urlValues := make(url.Values)
  630. // Set part number marker.
  631. urlValues.Set("part-number-marker", fmt.Sprintf("%d", partNumberMarker))
  632. // Set upload id.
  633. urlValues.Set("uploadId", uploadID)
  634. // maxParts should be 1000 or less.
  635. if maxParts == 0 || maxParts > 1000 {
  636. maxParts = 1000
  637. }
  638. // Set max parts.
  639. urlValues.Set("max-parts", fmt.Sprintf("%d", maxParts))
  640. // Execute GET on objectName to get list of parts.
  641. resp, err := c.executeMethod(context.Background(), "GET", requestMetadata{
  642. bucketName: bucketName,
  643. objectName: objectName,
  644. queryValues: urlValues,
  645. contentSHA256Hex: emptySHA256Hex,
  646. })
  647. defer closeResponse(resp)
  648. if err != nil {
  649. return ListObjectPartsResult{}, err
  650. }
  651. if resp != nil {
  652. if resp.StatusCode != http.StatusOK {
  653. return ListObjectPartsResult{}, httpRespToErrorResponse(resp, bucketName, objectName)
  654. }
  655. }
  656. // Decode list object parts XML.
  657. listObjectPartsResult := ListObjectPartsResult{}
  658. err = xmlDecoder(resp.Body, &listObjectPartsResult)
  659. if err != nil {
  660. return listObjectPartsResult, err
  661. }
  662. return listObjectPartsResult, nil
  663. }