Created
May 26, 2022 02:49
-
-
Save roberthamel/669d98971cdbbbc38c433458e7d094fc to your computer and use it in GitHub Desktop.
golang download progress
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://golangcode.com/download-a-file-with-progress/ | |
package main | |
import ( | |
"fmt" | |
"io" | |
"net/http" | |
"os" | |
"strings" | |
"github.com/dustin/go-humanize" | |
) | |
// WriteCounter counts the number of bytes written to it. It implements to the io.Writer interface | |
// and we can pass this into io.TeeReader() which will report progress on each write cycle. | |
type WriteCounter struct { | |
Total uint64 | |
} | |
func (wc *WriteCounter) Write(p []byte) (int, error) { | |
n := len(p) | |
wc.Total += uint64(n) | |
wc.PrintProgress() | |
return n, nil | |
} | |
func (wc WriteCounter) PrintProgress() { | |
// Clear the line by using a character return to go back to the start and remove | |
// the remaining characters by filling it with spaces | |
fmt.Printf("\r%s", strings.Repeat(" ", 35)) | |
// Return again and print current status of download | |
// We use the humanize package to print the bytes in a meaningful way (e.g. 10 MB) | |
fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total)) | |
} | |
func main() { | |
fmt.Println("Download Started") | |
fileUrl := "https://upload.wikimedia.org/wikipedia/commons/d/d6/Wp-w4-big.jpg" | |
err := DownloadFile("avatar.jpg", fileUrl) | |
if err != nil { | |
panic(err) | |
} | |
fmt.Println("Download Finished") | |
} | |
// DownloadFile will download a url to a local file. It's efficient because it will | |
// write as it downloads and not load the whole file into memory. We pass an io.TeeReader | |
// into Copy() to report progress on the download. | |
func DownloadFile(filepath string, url string) error { | |
// Create the file, but give it a tmp file extension, this means we won't overwrite a | |
// file until it's downloaded, but we'll remove the tmp extension once downloaded. | |
out, err := os.Create(filepath + ".tmp") | |
if err != nil { | |
return err | |
} | |
// Get the data | |
resp, err := http.Get(url) | |
if err != nil { | |
out.Close() | |
return err | |
} | |
defer resp.Body.Close() | |
// Create our progress reporter and pass it to be used alongside our writer | |
counter := &WriteCounter{} | |
if _, err = io.Copy(out, io.TeeReader(resp.Body, counter)); err != nil { | |
out.Close() | |
return err | |
} | |
// The progress use the same line so print a new line once it's finished downloading | |
fmt.Print("\n") | |
// Close the file without defer so it can happen before Rename() | |
out.Close() | |
if err = os.Rename(filepath+".tmp", filepath); err != nil { | |
return err | |
} | |
return nil | |
// $ go run download-file.go | |
// Download Started | |
// Downloading... 111 MB complete | |
// Download Finished | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment