我正在尝试从S3下载文件并将该文件上传到S3中的另一个存储桶。复制API在这里不起作用,因为我被告知不要使用它。
从S3获取对象的response.Body
为io.ReadCloser
并上传该文件,有效负载的Body
为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
?
答案 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.ReadSeeker
或io.Reader
获取io.ReadCloser
,您需要一些能够缓存所有数据的内容,以便根据请求可以寻求任何地方。
此缓存机制可能正如您所提到的那样将其写入文件,或者您可以使用ioutil.ReadAll()
将所有内容读入内存,[]byte
,然后您可以使用bytes.NewReader()
从io.ReadSeeker
获取[]byte
。当然这有其局限性:所有内容都必须适合内存,而且您可能也不想为此文件复制操作保留该内存量。
总而言之,io.Seeker
或io.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
。