LoginSignup
4
1

More than 5 years have passed since last update.

io.Reader の進捗状況

Last updated at Posted at 2018-08-24

(io.Reader).Read を「フック」することで、Read の進捗状況を監視する方法の紹介です。

例えば、HTTP でのファイルダウンロードの進捗状況を画面に表示したいという場合に使えます。

方法

(段階的に解説します)

手始めに io.Reader をラッピングするような構造体を定義します。
Go では、構造体のフィールド(無名)として構造体やインターフェイスを埋め込むと、埋め込んだ側の構造体が埋め込まれた側のメソッドの実装を引き継ぐことができます。

io.Reader を埋め込んだ構造体は、自身が io.Reader を実装したかのように振る舞います。

無名フィールドとして埋め込む

type MyReader struct {
    io.Reader
}

func a() {
    file, _ := os.Open("hoge.txt")
    defer file.Close()

    r := MyReader {
        Reader: file,
    }

    content, _ := ioutil.ReadAll(r) // r.Read が実装されていることになっている
}

MyReader.Read というメソッドは実装していません。
しかし MyReader.Reader として io.Reader を埋め込んでいるため、MyReader に対して ioutil.ReadAll を呼び出せます。

MyReader.Reader として埋め込まれた *os.File のメソッド Read が呼び出されます。

埋め込み先でフックする

ここで、メソッド MyReader.Read を定義してみます。

func (r *MyReader) Read(p []byte) (n int, err error) {
    return r.Reader.Read(p)
}

ここでは、埋め込んだ Reader のメソッド Read を明示的に呼び出しているだけです。
当然、この状態の MyReaderioutil.ReadAll に渡せます。

そして、r.Reader.Read(p) の前後に処理を挟むことで、加工やらフィルタリングやら、進捗状況の更新やらができるようになるわけです。

作ってみた

例によってパッケージを作ってみました。

progio

利用例
progreader := progio.NewReader(
    response.Body,
    func(p int64) {
        fmt.Printf("%d%%  ", p)
    },
    progio.Percent(response.ContentLength, 5/* % ごとに上のfuncを呼び出す */),
)

ちなみに Writer もあります。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1