edo1z blog

プログラミングなどに関するブログです

Go - ダウンロードとProgress bar表示

参考:Code-Hex/pget

rangeというのをheaderにセットすると、途中から途中までをダウンロードできるのか。これで分割ダウンロードとかリジュームとかできるらしい。pgetみながら自分でも一応簡単なダウンロード+Progress barをつくってみた。

main.go

package main

import (
    "fmt"
    "net/http"
    "log"
    "os"
    "io"
)

var target_url = "https://www.imagemagick.org/download/linux/CentOS/x86_64/ImageMagick-7.0.6-0.x86_64.rpm"
var out_dir = "."

type Target struct {
    url string
    dir_path string
    file_name string
    file_size int64
}

func main() {
    fmt.Println("test file download")
    target := Target{
        url: target_url,
        dir_path: out_dir,
    }
    check(&target)
    download(&target)
}

func check(t *Target) {
    fmt.Println("checking url...")
    res, err := http.Head(t.url)
    chkErr(err)
    if res.Header.Get("Accept-Ranges") != "bytes" {
        log.Fatal("not supported range access.")
    }
    if res.ContentLength <= 0 {
        log.Fatal("invalid content length.")
    }
    t.file_size = int64(res.ContentLength)
    t.file_name = GetFileName(t.url)
    fmt.Println("check ok.")
}

func download(t *Target){
    fmt.Println("downloading...")
    req, err := http.NewRequest("GET", t.url, nil)
    chkErr(err)
    req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", 0, t.file_size))
    res, err := http.DefaultClient.Do(req)
    chkErr(err)
    defer res.Body.Close()

    out_path := GetFilePath(t.dir_path, t.file_name)
    out, err := os.Create(out_path)
    chkErr(err)
    defer out.Close()
    ch := make(chan int)
    go ProgressBar(t, &amp;out_path, ch)
    io.Copy(out, res.Body)
    <- ch
}

func chkErr(err error){
    if err != nil {
        log.Fatal(err)
    }
}

util.go

package main

import (
    "gopkg.in/cheggaaa/pb.v2"
    "time"
    "net/url"
    "path/filepath"
    "os"
)

func GetFileName(u string) string {
    result, err := url.Parse(u)
    chkErr(err)
    return filepath.Base(result.Path)
}

func GetFilePath(d, f string) string {
    return filepath.Join(d, f)
}

func ProgressBar(t *Target, file_path *string, ch chan int){
    file_size := t.file_size
    now_size := int64(0)
    bar := pb.Start64(file_size)
    for {
        fi, err := os.Stat(*file_path)
        chkErr(err)
        now_size = fi.Size()
        if now_size < file_size {
            bar.SetCurrent(now_size)
        } else {
            bar.SetCurrent(file_size)
            bar.Finish()
            ch <- 1
            break
        }
        time.Sleep(100 * time.Millisecond)
    }
}