$30 off During Our Annual Pro Sale. View Details »

Golangでwget劣化クローンをつくる / wget clone with golang

kazto
June 15, 2019

Golangでwget劣化クローンをつくる / wget clone with golang

TeckUPもくもく会#2の成果報告です。

kazto

June 15, 2019
Tweet

More Decks by kazto

Other Decks in Programming

Transcript

  1. Golangで
    wget劣化クローンをつくる
    2019/06/15 TeckUpもくもく会
    kazto

    View Slide

  2. お題目
    • HTTPでGETする
    • コマンド引数を入手する
    • 並行処理
    • HTTPで分割ダウンロード
    • HTTPで分割ダウンロードが可能か調べる(HEAD)
    • 分割の範囲を決める

    View Slide

  3. HTTPでGETする
    • “net/http”をインポート
    • パラメータつきは、もうちょっと面倒
    url := flag.Args()[0]
    res, err := http.Get(url)
    client := &http.Client{}
    req, err := http.NewRequest("GET", url, nil)
    q := req.URL.Query()
    q.Add("api_key", "key_from_environment_or_flag")
    q.Add("another_thing", "foo & bar")
    req.URL.RawQuery = q.Encode()
    res, err := client.Do(req)

    View Slide

  4. コマンド引数を入手する
    • “flag”をインポート
    func parseOptions() map[string]string {
    pn := flag.String("n", "1", "Number of Download Channels")
    px := flag.String("X", "GET", "HTTP Request Method")
    flag.Parse()
    result := map[string]string{
    "number": *pn,
    "method": *px,
    }
    return result
    }

    View Slide

  5. Golangと言えば、並行処理でしょ
    • goroutineをchannel、WaitGroupを使って待ち合わせる
    wg := new(sync.WaitGroup)
    isFin := make(chan int, n)
    fmt.Println("start")
    for i := 0; i < n; i++ {
    wg.Add(1)
    go func(n int, isFin chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    time.Sleep(1 * time.Second)
    isFin <- n
    }(i, isFin, wg)
    }
    wg.Wait()
    close(isFin)
    for {
    v, ok := <-isFin
    if !ok {
    break
    }
    fmt.Println(v)
    }

    View Slide

  6. http.Getを深掘り(Goあんま関係ない)
    • 分割ダウンロードを実装したい
    • ヘッダに「Range: bytes=0-499」などと範囲を指定してリクエストする
    • レスポンスヘッダに以下のような感じでヘッダが付加されて返ってくる
    • Content-Length: 500
    • Content-Ranges: 0-499/1000
    • HEADリクエストでサーバ側が範囲指定を許可しているか確認してからの方がよさそう
    • Accept-Ranges: bytes
    • Content-Length: 146515

    View Slide

  7. http.Headで分割可能か調べる
    • エラー処理は下記では省略
    func hasAcceptRangesBytes(url string) bool {
    res, _ := http.Head(url)
    acceptRanges, _ := res.Header["Accept-Ranges"]
    for _, v := range acceptRanges {
    if v == "bytes" {
    return true
    }
    }
    return false
    }

    View Slide

  8. 分割の範囲を決める
    • 単純な整数の割り算
    func makeRanges(num int, length int) []string {
    result := []string{}
    // 整数同士の除算の結果は整数
    div := length / num
    s := 0
    e := div
    for length > 0 {
    str := fmt.Sprintf("Range: bytes=%d-%d", s, e)
    s = e + 1
    length -= div
    if length < 0 {
    e = s + div + length
    } else {
    e = s + div
    }
    result = append(result, str)
    }
    return result
    }

    View Slide