web-dev-qa-db-ja.com

同じio.Readerから複数回読み取る方法

画像を含むrequest.Body(type io.ReadCloser)を使用したい。

ioutil.ReadAll()を使用したくない

次の例のように、リーダーの複数のインスタンスを作成してみました

package main

import (
    "io/ioutil"
    "log"
    "strings"
)


func main() {
    r := strings.NewReader("some io.Reader stream to be read\n")
    a := &r
    b := &r
    log.Println(ioutil.ReadAll(*a))
    log.Println(ioutil.ReadAll(*b))

}

ただし、2回目の呼び出しでは常にnilになります。

同じリーダーに複数の個別の参照を渡すにはどうすればよいですか?

21
Abhishek Soni

io.Reader はストリームのように扱われます。このため、二度読むことはできません。入ってくるTCP接続を想像してください。入ってくるものを巻き戻すことはできません。

ただし、 io.TeeReader ストリームを複製するには:

package main

import (
    "bytes"
    "io"
    "io/ioutil"
    "log"
    "strings"
)

func main() {
    r := strings.NewReader("some io.Reader stream to be read\n")
    var buf bytes.Buffer
    tee := io.TeeReader(r, &buf)

    log.Println(ioutil.ReadAll(tee))
    log.Println(ioutil.ReadAll(&buf)) 
}

Go Playground の例

40
TheHippo

ReadAllを呼び出すとバッファが空になるため、2番目の呼び出しは常に何も返しません。できることは、ReadAllの結果を保存し、それを関数で再利用することです。例えば:

bytes, _ := ioutil.ReadAll(r);
log.Println(string(bytes))
4
laurent

@TheHippoの答えは正しいです私はこれを追加したいだけです(しかし、私は49の評判しか持っていないので追加できませんでした:(バッファは空になります。

1
mrclx

技術的には、1人のリーダーで複数回読むことはできません。

  • 異なる参照を作成する場合でもbut
  • 一度読むと、すべての参照によって参照される同じオブジェクトになります。
  • そのため、コンテンツを読み取って1つの変数に保存するだけです。
  • その後、必要な回数だけその変数を使用します。

これは2回印刷されます。

package main

import (
    "io/ioutil"
    "log"
    "strings"
)

func main() {
    r := strings.NewReader("some io.Reader stream to be read\n")
    stringData, _ := ioutil.ReadAll(r)
    log.Println(stringData)
    log.Println(stringData)
}
1
Vijay Gurunanee