api-list.go 23 KB

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