Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

248b3a861850209cf980a47dc1d5f3f2?s=47 kazto
June 15, 2019

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

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

248b3a861850209cf980a47dc1d5f3f2?s=128

kazto

June 15, 2019
Tweet

Transcript

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

  2. お題目 • HTTPでGETする • コマンド引数を入手する • 並行処理 • HTTPで分割ダウンロード •

    HTTPで分割ダウンロードが可能か調べる(HEAD) • 分割の範囲を決める
  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)
  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 }
  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) }
  6. http.Getを深掘り(Goあんま関係ない) • 分割ダウンロードを実装したい • ヘッダに「Range: bytes=0-499」などと範囲を指定してリクエストする • レスポンスヘッダに以下のような感じでヘッダが付加されて返ってくる • Content-Length:

    500 • Content-Ranges: 0-499/1000 • HEADリクエストでサーバ側が範囲指定を許可しているか確認してからの方がよさそう • Accept-Ranges: bytes • Content-Length: 146515
  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 }
  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 }