Go製コマンドpgetをライブラリとして使いたい！

## やりたいこと

「[高速でダウンロードできるツールを Go で実装した。 - Qiita](https://qiita.com/codehex/items/d0a500ac387d39a34401)」で有名な[`pget`](https://github.com/Code-Hex/pget)を使えば、並列にダンロードできた`wget`とか`curl`より速くダウンロードできていつも助かってます。やっぱり**速いは正義**です！

この`pget`を自分のGo製のアプリケーションに組み込んで、高速なダウンロードの恩恵に預かりたいです！

## ライブラリとしてpgetを使う

うまくいくまで、少し時間がかかりましたが、分かればとても簡単でした。

以下に完全なコードの例を載せます。

以下のコードの挙動は、
<http://releases.ubuntu.com/16.04/ubuntu-16.04.4-desktop-amd64.iso.zsync>のisoファイルを`./SavedFileName.dat`としてカレントディレクトリに保存します。

```go
import (
  "fmt"
  "github.com/Code-Hex/pget"
)

// pgetを使って並列にダウンロードする
func ParallelDownload(url string, dirPath string, fileName string) error {
  pg := pget.New()
  pg.URLs = []string{url}
  pg.TargetDir = dirPath
  pg.Utils.SetFileName(fileName)

  if err := pg.Checking(); err != nil {
    return err
  }

  if err := pg.Download(); err != nil {
    return err
  }

  if err := pg.Utils.BindwithFiles(pg.Procs); err != nil {
    return err
  }

  return nil
}

func main() {
  url      := "http://releases.ubuntu.com/16.04/ubuntu-16.04.4-desktop-amd64.iso.zsync"
  dirPath  := ""
  fileName := "SavedFileName.dat"
  if err := ParallelDownload(url, dirPath, fileName); err != nil {
    fmt.Println(err)
  }
}

```
### 簡単な説明

* `pg.URLs = ...`のところでダウンロードしたいURLを指定します
* `pg.TargetDir = ""`は空文字だと`$PWD`にダウンロードされることになります
* `pg.Utils.SetFileName(fileName)`はダウンロードされたあとのファイル名になります。
    * (`ubuntu-16.04.4-desktop-amd64.iso.zsync`が`./SavedFileName.dat`に保存されます)

* `pg.Checking()`でContent-Lengthとかを手に入れたりしているのです
* `pg.Download()`で並列ダウンロードの処理が走って
* `pg.Utils.BindwithFiles(...)`で並列にダウンロードしたものをくっつける処理を担当していそうです


## もっとオプションの機能を使いたいとき

オプションの機能を使いたいときは、`<repo>/pget.go`の`Ready()`が参考になりそうです。以下の変数`pget`は上のコードでは`pg`に対応してます。

`pg.Procs = 4`などとすれば、分割数を変更したりできそうです。
これらの設定は`pg.Checking()`の上に記述すればいいと思います。


```go
// Ready method define the variables required to Download.
func (pget *Pget) Ready() error {
	if procs := os.Getenv("GOMAXPROCS"); procs == "" {
		runtime.GOMAXPROCS(pget.Procs)
	}

	var opts Options
	if err := pget.parseOptions(&opts, os.Args[1:]); err != nil {
		return errors.Wrap(err, "failed to parse command line args")
	}

	if opts.Trace {
		pget.Trace = opts.Trace
	}

	if opts.Procs > 2 {
		pget.Procs = opts.Procs
	}

	if opts.Timeout > 0 {
		pget.timeout = opts.Timeout
	}

	if err := pget.parseURLs(); err != nil {
		return errors.Wrap(err, "failed to parse of url")
	}

	if opts.Output != "" {
		pget.Utils.SetFileName(opts.Output)
	}

	if opts.UserAgent != "" {
		pget.useragent = opts.UserAgent
	}

	if opts.Referer != "" {
		pget.referer = opts.Referer
	}

	if opts.TargetDir != "" {
		info, err := os.Stat(opts.TargetDir)
		if err != nil {
			if !os.IsNotExist(err) {
				return errors.Wrap(err, "target dir is invalid")
			}

			if err := os.MkdirAll(opts.TargetDir, 0755); err != nil {
				return errors.Wrapf(err, "failed to create diretory at %s", opts.TargetDir)
			}

		} else if !info.IsDir() {
			return errors.New("target dir is not a valid directory")
		}
		opts.TargetDir = strings.TrimSuffix(opts.TargetDir, "/")
	}
	pget.TargetDir = opts.TargetDir

	return nil
}
```
(from: <https://github.com/Code-Hex/pget/blob/9294f7465fa777e5d9400500691e244a9a9ff225/pget.go#L88>)

## 最後に

Goのプログラムから`pget`を使って、高速にダウンロードできるようになりました。本来`pget`はCLIツールだと思うので、ライブラリとして使うことが想定されているかはわかりません。プログレスバーを消せるようにしたり、アクセスできないフィールドにアクセスするためには、pgetのコード自体に変更をする必要があるかもしれません。

今後の変更によってなにか変わるかもしれません。使ったバージョンは0.0.6のrevisionは[7bace10b201ad40c4d9a37f0155d479f369bb918](https://github.com/Code-Hex/pget/tree/7bace10b201ad40c4d9a37f0155d479f369bb918)です。