Skip to content

Instantly share code, notes, and snippets.

@albulescu
Created March 25, 2016 09:44
Show Gist options
  • Save albulescu/e61979cc852e4ee8f49c to your computer and use it in GitHub Desktop.
Save albulescu/e61979cc852e4ee8f49c to your computer and use it in GitHub Desktop.
golang download file with progress
package main
/**
* @website http://albulescu.ro
* @author Cosmin Albulescu <cosmin@albulescu.ro>
*/
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
"os"
"path"
"strconv"
"time"
)
func PrintDownloadPercent(done chan int64, path string, total int64) {
var stop bool = false
for {
select {
case <-done:
stop = true
default:
file, err := os.Open(path)
if err != nil {
log.Fatal(err)
}
fi, err := file.Stat()
if err != nil {
log.Fatal(err)
}
size := fi.Size()
if size == 0 {
size = 1
}
var percent float64 = float64(size) / float64(total) * 100
fmt.Printf("%.0f", percent)
fmt.Println("%")
}
if stop {
break
}
time.Sleep(time.Second)
}
}
func DownloadFile(url string, dest string) {
file := path.Base(url)
log.Printf("Downloading file %s from %s\n", file, url)
var path bytes.Buffer
path.WriteString(dest)
path.WriteString("/")
path.WriteString(file)
start := time.Now()
out, err := os.Create(path.String())
if err != nil {
fmt.Println(path.String())
panic(err)
}
defer out.Close()
headResp, err := http.Head(url)
if err != nil {
panic(err)
}
defer headResp.Body.Close()
size, err := strconv.Atoi(headResp.Header.Get("Content-Length"))
if err != nil {
panic(err)
}
done := make(chan int64)
go PrintDownloadPercent(done, path.String(), int64(size))
resp, err := http.Get(url)
if err != nil {
panic(err)
}
defer resp.Body.Close()
n, err := io.Copy(out, resp.Body)
if err != nil {
panic(err)
}
done <- n
elapsed := time.Since(start)
log.Printf("Download completed in %s", elapsed)
}
func main() {
DownloadFile("https://wordpress.org/wordpress-4.4.2.zip","./")
}
@eswardatt
Copy link

who will give url instead of path ?? Waste Code

@winnchen
Copy link

who will give url instead of path ?? Waste Code

Please be polite.

@xzyaoi
Copy link

xzyaoi commented Jan 19, 2020

Cool and useful for me! Thanks

@deven96
Copy link

deven96 commented Feb 12, 2020

This was helpful.. Thanks!

@stefpap
Copy link

stefpap commented Oct 13, 2021

Good stuff!
I had an issue after downloading a file I couldn't move it to another folder because of the following error: The process cannot access the file because it is being used by another process. and realised you probably need to file.Close(). I also moved os.Open(path) outside of the for loop.

Now it looks something like this:

func PrintDownloadPercent(done chan int64, path string, total int64) {
	var stop bool = false
        file, err := os.Open(path)
	if err != nil {
		log.Fatal(err)
	}
        defer file.Close()
	for {
		select {
		case <-done:
			stop = true
		default:
			fi, err := file.Stat()
			if err != nil {
				log.Fatal(err)
			}

			size := fi.Size()
			if size == 0 {
				size = 1
			}

			var percent float64 = float64(size) / float64(total) * 100
			fmt.Printf("%.0f", percent)
			fmt.Println("%")
		}

		if stop {
			break
		}
		time.Sleep(time.Second)
	}
}

@andrebenedetti
Copy link

Nice one =). Thanks for sharing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment