【问题标题】:How to read multiple times from same io.Reader如何从同一个 io.Reader 读取多次
【发布时间】:2017-02-09 00:03:35
【问题描述】:

我想使用包含图像的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))

}

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

请帮助我如何为同一个读者传递多个单独的参考?

【问题讨论】:

  • 按您要求的方式撤消。使用io.TeeReader 捕获您阅读的内容以供进一步参考,例如在bytes.Buffer.

标签: go reader


【解决方案1】:

当您调用ReadAll 时,它将清空缓冲区,因此第二次调用将始终不返回任何内容。你可以做的是保存ReadAll 的结果并在你的函数中重用它。例如:

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

【讨论】:

  • 如果阅读器包含非常大的数据,例如 2G 字节,这是一种不好的方法。
【解决方案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)
}

【讨论】:

  • 我想知道为什么不这样做:-)
  • @AbhishekSoni:io.Reader 是一个流。一旦你完成了流,就没有更多了。如果您读取了流的一部分,则下一次读取会从您离开的地方继续。
【解决方案3】:

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 读取,否则缓冲区将为空。

【讨论】:

  • 另外,如果流的数据很大——比如1GB——不要使用ReadAll。相反 - 由于 OP 想要先将其写入文件 - 使用 err, _ := io.Copy(fh, tee)
【解决方案4】:

然后,当您从 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))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-24
    • 1970-01-01
    • 1970-01-01
    • 2017-08-27
    • 2017-03-05
    • 2022-06-13
    相关资源
    最近更新 更多