我如何从io.ReadCloser转到io.ReadSeeker?

时间:2016-06-09 06:18:11

标签: go

我正在尝试从S3下载文件并将该文件上传到S3中的另一个存储桶。复制API在这里不起作用,因为我被告知不要使用它。

从S3获取对象的response.Bodyio.ReadCloser并上传该文件,有效负载的Bodyio.ReadSeeker

我能解决这个问题的唯一方法是将response.Body保存到文件中,然后将该文件作为io.ReadSeeker传递。这需要先将整个文件写入磁盘,然后从磁盘读取整个文件,这听起来非常错误。

我想做的是:

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

问题是,如何以最有效的方式从io.ReadCloser转到io.ReadSeeker

2 个答案:

答案 0 :(得分:12)

io.ReadSeeker是对基本Read()Seek()方法进行分组的界面。 Seek()方法的定义:

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

Seek()方法的实现需要能够在源中寻找任何地方,这需要所有源可用或可重现。文件是一个很好的例子,文件永久保存到您的磁盘上,任何时候都可以读取它。

实现

response.Body以从底层TCP连接读取。从底层TCP连接读取将为您提供另一方客户端发送给您的数据。数据未缓存,客户端将不会根据请求再次向您发送数据。这就是response.Body未实现io.Seeker(以及io.ReadSeeker)的原因。

因此,为了从io.ReadSeekerio.Reader获取io.ReadCloser,您需要一些能够缓存所有数据的内容,以便根据请求可以寻求任何地方。

此缓存机制可能正如您所提到的那样将其写入文件,或者您可以使用ioutil.ReadAll()将所有内容读入内存,[]byte,然后您可以使用bytes.NewReader()io.ReadSeeker获取[]byte。当然这有其局限性:所有内容都必须适合内存,而且您可能也不想为此文件复制操作保留该内存量。

总而言之,io.Seekerio.ReadSeeker的实现需要所有源数据都可用,因此最好的办法是将其写入文件,或者将所有源文件写入{{ {1}}并流式传输该字节切片的内容。

答案 1 :(得分:9)

作为替代方案,请使用github.com/aws/aws-sdk-go/service/s3/s3manager.Uploader,其中io.Reader作为输入。

我认为PutObject使用io.ReadSeeker代替io.Reader的原因是对s3的请求需要签名(并且内容长度有限),但您可以&#39 ; t生成签名,直到获得所有数据。 stream-y的方法是在输入时将输入缓冲到块中,并使用multipart上传api分别上传每个块。这是(我认为)幕后的s3manager.Uploader