Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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)

Slide 4

Slide 4 text

コマンド引数を入手する • “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 }

Slide 5

Slide 5 text

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) }

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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 }

Slide 8

Slide 8 text

分割の範囲を決める • 単純な整数の割り算 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 }