github.com/aavshr/aws-sdk-go@v1.41.3/example/service/s3/getObjectWithProgress/getObjectWithProgress.go (about) 1 //go:build example 2 // +build example 3 4 package main 5 6 import ( 7 "fmt" 8 "io" 9 "io/ioutil" 10 "log" 11 "os" 12 "strings" 13 "sync/atomic" 14 15 "github.com/aavshr/aws-sdk-go/aws" 16 "github.com/aavshr/aws-sdk-go/aws/session" 17 "github.com/aavshr/aws-sdk-go/service/s3" 18 "github.com/aavshr/aws-sdk-go/service/s3/s3manager" 19 ) 20 21 // progressWriter tracks the download progress of a file from S3 to a file 22 // as the writeAt method is called, the byte size is added to the written total, 23 // and then a log is printed of the written percentage from the total size 24 // it looks like this on the command line: 25 // 2019/02/22 12:59:15 File size:35943530 downloaded:16360 percentage:0% 26 // 2019/02/22 12:59:15 File size:35943530 downloaded:16988 percentage:0% 27 // 2019/02/22 12:59:15 File size:35943530 downloaded:33348 percentage:0% 28 type progressWriter struct { 29 written int64 30 writer io.WriterAt 31 size int64 32 } 33 34 func (pw *progressWriter) WriteAt(p []byte, off int64) (int, error) { 35 atomic.AddInt64(&pw.written, int64(len(p))) 36 37 percentageDownloaded := float32(pw.written*100) / float32(pw.size) 38 39 fmt.Printf("File size:%d downloaded:%d percentage:%.2f%%\r", pw.size, pw.written, percentageDownloaded) 40 41 return pw.writer.WriteAt(p, off) 42 } 43 44 func byteCountDecimal(b int64) string { 45 const unit = 1000 46 if b < unit { 47 return fmt.Sprintf("%d B", b) 48 } 49 div, exp := int64(unit), 0 50 for n := b / unit; n >= unit; n /= unit { 51 div *= unit 52 exp++ 53 } 54 return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp]) 55 } 56 57 func getFileSize(svc *s3.S3, bucket string, prefix string) (filesize int64, error error) { 58 params := &s3.HeadObjectInput{ 59 Bucket: aws.String(bucket), 60 Key: aws.String(prefix), 61 } 62 63 resp, err := svc.HeadObject(params) 64 if err != nil { 65 return 0, err 66 } 67 68 return *resp.ContentLength, nil 69 } 70 71 func parseFilename(keyString string) (filename string) { 72 ss := strings.Split(keyString, "/") 73 s := ss[len(ss)-1] 74 return s 75 } 76 77 func main() { 78 if len(os.Args) < 2 { 79 log.Println("USAGE ERROR: AWS_REGION=us-east-1 go run getObjWithProgress.go bucket-name object-key") 80 return 81 } 82 83 bucket := os.Args[1] 84 key := os.Args[2] 85 86 filename := parseFilename(key) 87 88 sess, err := session.NewSession() 89 if err != nil { 90 panic(err) 91 } 92 93 s3Client := s3.New(sess) 94 downloader := s3manager.NewDownloader(sess) 95 size, err := getFileSize(s3Client, bucket, key) 96 if err != nil { 97 panic(err) 98 } 99 100 log.Println("Starting download, size:", byteCountDecimal(size)) 101 cwd, err := os.Getwd() 102 if err != nil { 103 panic(err) 104 } 105 106 temp, err := ioutil.TempFile(cwd, "getObjWithProgress-tmp-") 107 if err != nil { 108 panic(err) 109 } 110 tempfileName := temp.Name() 111 112 writer := &progressWriter{writer: temp, size: size, written: 0} 113 params := &s3.GetObjectInput{ 114 Bucket: aws.String(bucket), 115 Key: aws.String(key), 116 } 117 118 if _, err := downloader.Download(writer, params); err != nil { 119 log.Printf("Download failed! Deleting tempfile: %s", tempfileName) 120 os.Remove(tempfileName) 121 panic(err) 122 } 123 124 if err := temp.Close(); err != nil { 125 panic(err) 126 } 127 128 if err := os.Rename(temp.Name(), filename); err != nil { 129 panic(err) 130 } 131 132 fmt.Println() 133 log.Println("File downloaded! Available at:", filename) 134 }