33
44package actions
55
6+ // snippet-start:[gov2.s3.BucketBasics.complete]
7+ // snippet-start:[gov2.s3.BucketBasics.struct]
8+
69import (
710 "bytes"
811 "context"
@@ -11,6 +14,7 @@ import (
1114 "io"
1215 "log"
1316 "os"
17+ "time"
1418
1519 "github.com/aws/aws-sdk-go-v2/aws"
1620 "github.com/aws/aws-sdk-go-v2/feature/s3/manager"
@@ -19,9 +23,6 @@ import (
1923 "github.com/aws/smithy-go"
2024)
2125
22- // snippet-start:[gov2.s3.BucketBasics.complete]
23- // snippet-start:[gov2.s3.BucketBasics.struct]
24-
2526// BucketBasics encapsulates the Amazon Simple Storage Service (Amazon S3) actions
2627// used in the examples.
2728// It contains S3Client, an Amazon S3 service client that is used to perform bucket
@@ -36,12 +37,24 @@ type BucketBasics struct {
3637
3738// ListBuckets lists the buckets in the current account.
3839func (basics BucketBasics ) ListBuckets (ctx context.Context ) ([]types.Bucket , error ) {
39- result , err := basics .S3Client .ListBuckets (ctx , & s3.ListBucketsInput {})
40+ var err error
41+ var output * s3.ListBucketsOutput
4042 var buckets []types.Bucket
41- if err != nil {
42- log .Printf ("Couldn't list buckets for your account. Here's why: %v\n " , err )
43- } else {
44- buckets = result .Buckets
43+ bucketPaginator := s3 .NewListBucketsPaginator (basics .S3Client , & s3.ListBucketsInput {})
44+ for bucketPaginator .HasMorePages () {
45+ output , err = bucketPaginator .NextPage (ctx )
46+ if err != nil {
47+ var apiErr smithy.APIError
48+ if errors .As (err , & apiErr ) && apiErr .ErrorCode () == "AccessDenied" {
49+ fmt .Println ("You don't have permission to list buckets for this account." )
50+ err = apiErr
51+ } else {
52+ log .Printf ("Couldn't list buckets for your account. Here's why: %v\n " , err )
53+ }
54+ break
55+ } else {
56+ buckets = append (buckets , output .Buckets ... )
57+ }
4558 }
4659 return buckets , err
4760}
@@ -89,8 +102,21 @@ func (basics BucketBasics) CreateBucket(ctx context.Context, name string, region
89102 },
90103 })
91104 if err != nil {
92- log .Printf ("Couldn't create bucket %v in Region %v. Here's why: %v\n " ,
93- name , region , err )
105+ var owned * types.BucketAlreadyOwnedByYou
106+ var exists * types.BucketAlreadyExists
107+ if errors .As (err , & owned ) {
108+ log .Printf ("You already own bucket %s.\n " , name )
109+ err = owned
110+ } else if errors .As (err , & exists ) {
111+ log .Printf ("Bucket %s already exists.\n " , name )
112+ err = exists
113+ }
114+ } else {
115+ err = s3 .NewBucketExistsWaiter (basics .S3Client ).Wait (
116+ ctx , & s3.HeadBucketInput {Bucket : aws .String (name )}, time .Minute )
117+ if err != nil {
118+ log .Printf ("Failed attempt to wait for bucket %s to exist.\n " , name )
119+ }
94120 }
95121 return err
96122}
@@ -112,8 +138,21 @@ func (basics BucketBasics) UploadFile(ctx context.Context, bucketName string, ob
112138 Body : file ,
113139 })
114140 if err != nil {
115- log .Printf ("Couldn't upload file %v to %v:%v. Here's why: %v\n " ,
116- fileName , bucketName , objectKey , err )
141+ var apiErr smithy.APIError
142+ if errors .As (err , & apiErr ) && apiErr .ErrorCode () == "EntityTooLarge" {
143+ log .Printf ("Error while uploading object to %s. The object is too large.\n " +
144+ "To upload objects larger than 5GB, use the S3 console (160GB max)\n " +
145+ "or the multipart upload API (5TB max)." , bucketName )
146+ } else {
147+ log .Printf ("Couldn't upload file %v to %v:%v. Here's why: %v\n " ,
148+ fileName , bucketName , objectKey , err )
149+ }
150+ } else {
151+ err = s3 .NewObjectExistsWaiter (basics .S3Client ).Wait (
152+ ctx , & s3.HeadObjectInput {Bucket : aws .String (bucketName ), Key : aws .String (objectKey )}, time .Minute )
153+ if err != nil {
154+ log .Printf ("Failed attempt to wait for object %s to exist.\n " , objectKey )
155+ }
117156 }
118157 }
119158 return err
@@ -137,8 +176,20 @@ func (basics BucketBasics) UploadLargeObject(ctx context.Context, bucketName str
137176 Body : largeBuffer ,
138177 })
139178 if err != nil {
140- log .Printf ("Couldn't upload large object to %v:%v. Here's why: %v\n " ,
141- bucketName , objectKey , err )
179+ var apiErr smithy.APIError
180+ if errors .As (err , & apiErr ) && apiErr .ErrorCode () == "EntityTooLarge" {
181+ log .Printf ("Error while uploading object to %s. The object is too large.\n " +
182+ "The maximum size for a multipart upload is 5TB." , bucketName )
183+ } else {
184+ log .Printf ("Couldn't upload large object to %v:%v. Here's why: %v\n " ,
185+ bucketName , objectKey , err )
186+ }
187+ } else {
188+ err = s3 .NewObjectExistsWaiter (basics .S3Client ).Wait (
189+ ctx , & s3.HeadObjectInput {Bucket : aws .String (bucketName ), Key : aws .String (objectKey )}, time .Minute )
190+ if err != nil {
191+ log .Printf ("Failed attempt to wait for object %s to exist.\n " , objectKey )
192+ }
142193 }
143194
144195 return err
@@ -155,7 +206,13 @@ func (basics BucketBasics) DownloadFile(ctx context.Context, bucketName string,
155206 Key : aws .String (objectKey ),
156207 })
157208 if err != nil {
158- log .Printf ("Couldn't get object %v:%v. Here's why: %v\n " , bucketName , objectKey , err )
209+ var noKey * types.NoSuchKey
210+ if errors .As (err , & noKey ) {
211+ log .Printf ("Can't get object %s from bucket %s. No such key exists.\n " , objectKey , bucketName )
212+ err = noKey
213+ } else {
214+ log .Printf ("Couldn't get object %v:%v. Here's why: %v\n " , bucketName , objectKey , err )
215+ }
159216 return err
160217 }
161218 defer result .Body .Close ()
@@ -203,14 +260,25 @@ func (basics BucketBasics) DownloadLargeObject(ctx context.Context, bucketName s
203260
204261// CopyToFolder copies an object in a bucket to a subfolder in the same bucket.
205262func (basics BucketBasics ) CopyToFolder (ctx context.Context , bucketName string , objectKey string , folderName string ) error {
263+ objectDest := fmt .Sprintf ("%v/%v" , folderName , objectKey )
206264 _ , err := basics .S3Client .CopyObject (ctx , & s3.CopyObjectInput {
207265 Bucket : aws .String (bucketName ),
208266 CopySource : aws .String (fmt .Sprintf ("%v/%v" , bucketName , objectKey )),
209- Key : aws .String (fmt . Sprintf ( "%v/%v" , folderName , objectKey ) ),
267+ Key : aws .String (objectDest ),
210268 })
211269 if err != nil {
212- log .Printf ("Couldn't copy object from %v:%v to %v:%v/%v. Here's why: %v\n " ,
213- bucketName , objectKey , bucketName , folderName , objectKey , err )
270+ var notActive * types.ObjectNotInActiveTierError
271+ if errors .As (err , & notActive ) {
272+ log .Printf ("Couldn't copy object %s from %s because the object isn't in the active tier.\n " ,
273+ objectKey , bucketName )
274+ err = notActive
275+ }
276+ } else {
277+ err = s3 .NewObjectExistsWaiter (basics .S3Client ).Wait (
278+ ctx , & s3.HeadObjectInput {Bucket : aws .String (bucketName ), Key : aws .String (objectDest )}, time .Minute )
279+ if err != nil {
280+ log .Printf ("Failed attempt to wait for object %s to exist.\n " , objectDest )
281+ }
214282 }
215283 return err
216284}
@@ -227,8 +295,18 @@ func (basics BucketBasics) CopyToBucket(ctx context.Context, sourceBucket string
227295 Key : aws .String (objectKey ),
228296 })
229297 if err != nil {
230- log .Printf ("Couldn't copy object from %v:%v to %v:%v. Here's why: %v\n " ,
231- sourceBucket , objectKey , destinationBucket , objectKey , err )
298+ var notActive * types.ObjectNotInActiveTierError
299+ if errors .As (err , & notActive ) {
300+ log .Printf ("Couldn't copy object %s from %s because the object isn't in the active tier.\n " ,
301+ objectKey , sourceBucket )
302+ err = notActive
303+ }
304+ } else {
305+ err = s3 .NewObjectExistsWaiter (basics .S3Client ).Wait (
306+ ctx , & s3.HeadObjectInput {Bucket : aws .String (destinationBucket ), Key : aws .String (objectKey )}, time .Minute )
307+ if err != nil {
308+ log .Printf ("Failed attempt to wait for object %s to exist.\n " , objectKey )
309+ }
232310 }
233311 return err
234312}
@@ -239,16 +317,27 @@ func (basics BucketBasics) CopyToBucket(ctx context.Context, sourceBucket string
239317
240318// ListObjects lists the objects in a bucket.
241319func (basics BucketBasics ) ListObjects (ctx context.Context , bucketName string ) ([]types.Object , error ) {
242- result , err := basics .S3Client .ListObjectsV2 (ctx , & s3.ListObjectsV2Input {
320+ var err error
321+ var output * s3.ListObjectsV2Output
322+ input := & s3.ListObjectsV2Input {
243323 Bucket : aws .String (bucketName ),
244- })
245- var contents []types.Object
246- if err != nil {
247- log .Printf ("Couldn't list objects in bucket %v. Here's why: %v\n " , bucketName , err )
248- } else {
249- contents = result .Contents
250324 }
251- return contents , err
325+ var objects []types.Object
326+ objectPaginator := s3 .NewListObjectsV2Paginator (basics .S3Client , input )
327+ for objectPaginator .HasMorePages () {
328+ output , err = objectPaginator .NextPage (ctx )
329+ if err != nil {
330+ var noBucket * types.NoSuchBucket
331+ if errors .As (err , & noBucket ) {
332+ log .Printf ("Bucket %s does not exist.\n " , bucketName )
333+ err = noBucket
334+ }
335+ break
336+ } else {
337+ objects = append (objects , output .Contents ... )
338+ }
339+ }
340+ return objects , err
252341}
253342
254343// snippet-end:[gov2.s3.ListObjectsV2]
@@ -263,12 +352,32 @@ func (basics BucketBasics) DeleteObjects(ctx context.Context, bucketName string,
263352 }
264353 output , err := basics .S3Client .DeleteObjects (ctx , & s3.DeleteObjectsInput {
265354 Bucket : aws .String (bucketName ),
266- Delete : & types.Delete {Objects : objectIds },
355+ Delete : & types.Delete {Objects : objectIds , Quiet : aws . Bool ( true ) },
267356 })
268- if err != nil {
269- log .Printf ("Couldn't delete objects from bucket %v. Here's why: %v\n " , bucketName , err )
357+ if err != nil || len (output .Errors ) > 0 {
358+ log .Printf ("Error deleting objects from bucket %s.\n " , bucketName )
359+ if err != nil {
360+ var noBucket * types.NoSuchBucket
361+ if errors .As (err , & noBucket ) {
362+ log .Printf ("Bucket %s does not exist.\n " , bucketName )
363+ err = noBucket
364+ }
365+ } else if len (output .Errors ) > 0 {
366+ for _ , outErr := range output .Errors {
367+ log .Printf ("%s: %s\n " , * outErr .Key , * outErr .Message )
368+ }
369+ err = fmt .Errorf ("%s" , * output .Errors [0 ].Message )
370+ }
270371 } else {
271- log .Printf ("Deleted %v objects.\n " , len (output .Deleted ))
372+ for _ , delObjs := range output .Deleted {
373+ err = s3 .NewObjectNotExistsWaiter (basics .S3Client ).Wait (
374+ ctx , & s3.HeadObjectInput {Bucket : aws .String (bucketName ), Key : delObjs .Key }, time .Minute )
375+ if err != nil {
376+ log .Printf ("Failed attempt to wait for object %s to be deleted.\n " , * delObjs .Key )
377+ } else {
378+ log .Printf ("Deleted %s.\n " , * delObjs .Key )
379+ }
380+ }
272381 }
273382 return err
274383}
@@ -282,7 +391,21 @@ func (basics BucketBasics) DeleteBucket(ctx context.Context, bucketName string)
282391 _ , err := basics .S3Client .DeleteBucket (ctx , & s3.DeleteBucketInput {
283392 Bucket : aws .String (bucketName )})
284393 if err != nil {
285- log .Printf ("Couldn't delete bucket %v. Here's why: %v\n " , bucketName , err )
394+ var noBucket * types.NoSuchBucket
395+ if errors .As (err , & noBucket ) {
396+ log .Printf ("Bucket %s does not exist.\n " , bucketName )
397+ err = noBucket
398+ } else {
399+ log .Printf ("Couldn't delete bucket %v. Here's why: %v\n " , bucketName , err )
400+ }
401+ } else {
402+ err = s3 .NewBucketNotExistsWaiter (basics .S3Client ).Wait (
403+ ctx , & s3.HeadBucketInput {Bucket : aws .String (bucketName )}, time .Minute )
404+ if err != nil {
405+ log .Printf ("Failed attempt to wait for bucket %s to be deleted.\n " , bucketName )
406+ } else {
407+ log .Printf ("Deleted %s.\n " , bucketName )
408+ }
286409 }
287410 return err
288411}
0 commit comments