如何从同一个 io.Reader 中读取多次

How to read multiple times from same io.Reader

我想使用包含图像的 request.Body(type io.ReadCloser)

我不想使用 ioutil.ReadAll() 因为我想将这个正文直接写入文件并解码它,所以我只想使用对内容的引用来传递给进一步的功能电话,

我尝试创建 reader 的多个实例,如下所示

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

}

但在第二次调用中它总是导致 nil

请帮助我如何为同一个 reader 传递多个单独的引用?

当您调用 ReadAll 时,它会清空缓冲区,因此第二次调用总是 return 什么都没有。您可以做的是保存 ReadAll 的结果并在您的函数中重用它。例如:

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

从技术上讲,在一个 reader 上,您不能多次阅读。

  • 即使您创建了不同的引用但是
  • 当您阅读一次时,所有引用都将引用同一对象。
  • 所以您可以做的是读取内容并将其存储在一个变量中。
  • 然后根据需要多次使用该变量。

这将打印两次。

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

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

编辑: 正如@mrclx 指出的:您需要先从 TeeReader 读取,否则缓冲区将为空。

当您从 ioutil.ReadAll(r) 读取时,内容消失了。你不能再读一遍。 例如:

var response *http.Response

//Read the content
rawBody, err := ioutil.ReadAll(response.Body)
    if err != nil {
        t.Error(err)
    }

// Restore the io.ReadCloser to it's original state
response.Body = ioutil.NopCloser(bytes.NewBuffer(rawBody))