Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I go from io.ReadCloser to io.ReadSeeker?

Tags:

go

I'm trying to download a file from S3 and upload that file to another bucket in S3. Copy API won't work here because I've been told not to use it.

Getting an object from S3 has a response.Body that's an io.ReadCloser and to upload that file, the payload takes a Body that's an io.ReadSeeker.

The only way I can figure this out is by saving the response.Body to a file then passing that file as a io.ReadSeeker. This would require writing the entire file to disk first then reading the entire file from disk which sounds pretty wrong.

What I would like to do is:

resp, _ := conn.GetObject(&s3.GetObjectInput{Key: "bla"}) conn.PutObject(&s3.PutObjectInput{Body: resp.Body}) // resp.Body is an io.ReadCloser and the field type expects an io.ReadSeeker 

Question is, how do I go from an io.ReadCloser to an io.ReadSeeker in the most efficient way possible?

like image 808
Jeff Avatar asked Jun 09 '16 06:06

Jeff


People also ask

What is io ReadCloser?

You may use it when you have Read and Close methods, an example to show that you may use one common function to work with different types using io.ReadCloser : package main import ( "fmt" "io" "log" "os" ) func main() { f, err := os.Open("./main.go") if err != nil { log.Fatal(err) } doIt(f) doIt(os.Stdin) } func doIt( ...

What is io EOF in Golang?

It returns the number of bytes copied and an error if fewer bytes were read. The error is EOF only if no bytes were read. If an EOF happens after reading some but not all the bytes, ReadFull returns ErrUnexpectedEOF.

What is io writer?

The io.Writer interface is used by many packages in the Go standard library and it represents the ability to write a byte slice into a stream of data. More generically allows you to write data into something that implements the io.Writer interface.


1 Answers

io.ReadSeeker is the interface that groups the basic Read() and Seek() methods. The definition of the Seek() method:

Seek(offset int64, whence int) (int64, error) 

An implementation of the Seek() method requires to be able to seek anywhere in the source, which requires all the source to be available or reproducible. A file is a perfect example, the file is saved permanently to your disk and any part of it can be read at any time.

response.Body is implemented to read from the underlying TCP connection. Reading from the underlying TCP connection gives you the data that the client at the other side sends you. The data is not cached, and the client won't send you the data again upon request. That's why response.Body does not implement io.Seeker (and thus io.ReadSeeker either).

So in order to obtain an io.ReadSeeker from an io.Reader or io.ReadCloser, you need something that caches all the data, so that upon request it can seek to anywhere in that.

This caching mechanism may be writing it to a file as you mentioned, or you can read everything into memory, into a []byte using ioutil.ReadAll(), and then you can use bytes.NewReader() to obtain an io.ReadSeeker from a []byte. Of course this has its limitations: all the content must fit into memory, and also you might not want to reserve that amount of memory for this file copy operation.

All in all, an implementation of io.Seeker or io.ReadSeeker requires all the source data to be available, so your best bet is writing it to a file, or for small files reading all into a []byte and streaming the content of that byte slice.

like image 115
icza Avatar answered Sep 24 '22 07:09

icza