Skip to content

Instantly share code, notes, and snippets.

@Airr
Last active March 6, 2019 23:04
Show Gist options
  • Save Airr/355db430332a63479afa6d0a8a43f289 to your computer and use it in GitHub Desktop.
Save Airr/355db430332a63479afa6d0a8a43f289 to your computer and use it in GitHub Desktop.
[go-downloadFile] Download file with realtime status #go
/*
Original code: https://golangcode.com/download-a-file-with-progress/
I added Filesize field to WriteCounter struct, and set that
field to the ContentLength of the file to download.
Changed download file to 100MB test file
Changed status to show amount downloaded and total size of download
*/
package main
import (
"fmt"
"io"
"net/http"
"os"
"path"
"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
Filesize uint64
}
func main() {
// URL for file to download
fileURL := "http://ipv4.download.thinkbroadband.com/100MB.zip"
// Extract the filename
fileName := path.Base(fileURL)
// Show the file to be downloaded
fmt.Printf("Downloading \"%s\"\n", fileName)
err := DownloadFile(fileName, 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
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()
// Get the data
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// Create our progress reporter (with reported Filesize set) and pass it to be used alongside our writer
counter := &WriteCounter{Filesize: uint64(resp.ContentLength)}
_, err = io.Copy(out, io.TeeReader(resp.Body, counter))
if err != nil {
return err
}
// The progress outputs on a single line so print a new line once it's finished downloading
fmt.Print("\n")
return nil
}
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(" ", 30))
// 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("\rDownloaded %s of %s", humanize.Bytes(wc.Total), humanize.Bytes(wc.Filesize))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment