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

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


从 S3 获取对象有一个response.Bodythat's anio.ReadCloser并且要上传该文件,有效负载需要一个Bodythat's an io.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


哔哔one
浏览 519回答 2
2回答

肥皂起泡泡

io.ReadSeeker是分组基本Read()和Seek()方法的接口。方法的定义Seek():Seek(offset int64, whence int) (int64, error)该Seek()方法的实现需要能够在源中的任何位置寻找,这要求所有源都可用或可重现。文件就是一个很好的例子,该文件永久保存到您的磁盘中,并且可以随时读取其中的任何部分。response.Body被实现为从底层 TCP 连接中读取。从底层 TCP 连接中读取数据会为您提供另一端的客户端发送给您的数据。数据不会被缓存,客户端不会根据请求再次向您发送数据。这就是为什么response.Body不实施io.Seeker(因此也不实施io.ReadSeeker)。因此,为了io.ReadSeeker从io.Readeror中获取 an io.ReadCloser,您需要缓存所有数据的东西,以便根据请求它可以搜索到其中的任何位置。正如您提到的,这种缓存机制可能会将其写入文件,或者您可以将所有内容读入内存,读入[]byteusing ioutil.ReadAll(),然后您可以使用bytes.NewReader()从. 当然,这有其局限性:所有内容都必须适合内存,而且您可能不想为此文件复制操作保留该内存量。io.ReadSeeker[]byte总而言之,实现io.Seeker或io.ReadSeeker要求所有源数据都可用,因此最好的选择是将其写入文件,或者将小文件全部读入 a[]byte并流式传输该字节片的内容。

ibeautiful

作为替代方案,使用github.com/aws/aws-sdk-go/service/s3/s3manager.Uploader,它将 aio.Reader作为输入。我想PutObject采用 anio.ReadSeeker而不是 an的原因io.Reader是对 s3 的请求需要签名(并且具有内容长度),但是在拥有所有数据之前您无法生成签名。执行此操作的流式方法是将输入缓冲到块中,并使用分段上传 api 分别上传每个块。这是(我认为)s3manager.Uploader在幕后所做的。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go