Как читать несколько раз из одного 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))

}

но во втором вызове он всегда выводится в nil.

Пожалуйста, помогите мне, как я могу передать несколько отдельных ссылок для одного и того же читателя?

Ответ 1

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, иначе буфер будет пуст.

Ответ 2

Когда вы вызываете ReadAll, он будет пустым буфером, поэтому второй вызов всегда ничего не возвращает. Что вы можете сделать, это сохранить результат ReadAll и повторно использовать его в своих функциях. Например:

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

Ответ 3

Технически, на одном читателе вы не можете читать несколько раз.

  • Даже если вы создаете разные ссылки , но
  • когда вы читаете один раз, когда он будет тем же объектом, на который ссылаются все ссылки.
  • поэтому вы можете прочитать содержимое и сохранить его в одной переменной.
  • Затем используйте эту переменную столько раз, сколько хотите.

Это будет печататься дважды.

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

Ответ 4

@TheHippo ответ правильный, я просто хотел добавить это (но не смог добавить его, потому что у меня всего 49 репутации :(): важно, чтобы вы сначала использовали TeeReader, а после вы использовали буфер, куда копируется информация, в противном случае - второй. буфер будет пуст.