api-list.go 22 KB

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