aws-sdk-go-v2
でS3にオブジェクトをアップロードするには PutObject
が利用できます。
引数となる PutObjectInput
の Body
fieldに、アップロードしたいオブジェクトコンテンツを io.Reader
で渡すことができます。
例えば、下記例では bytes.Buffer
型の値を渡しています。
input := &s3.PutObjectInput{ Bucket: aws.String(bucketName), Key: aws.String(key), Body: bytes.NewBuffer(b) // b : object binary } resp, err := client.PutObject(ctx, input) if err != nil { return err }
ただしアップロードは失敗し、下記のようなerrorが返ってきます。
operation error S3: PutObject, failed to compute payload hash: failed to seek body to start, request stream is not seekable
error文言からは PutObject
がペイロードのハッシュを計算するため、 Body
に io.Seeker
を実装したstreamを渡さなければいけないことがわかります。
bytes.Buffer
は Seek(offset int64, whence int) (int64, error)
を実装していないので失敗します。
// Seeker is the interface that wraps the basic Seek method. // // Seek sets the offset for the next Read or Write to offset, // interpreted according to whence: // SeekStart means relative to the start of the file, // SeekCurrent means relative to the current offset, and // SeekEnd means relative to the end. // Seek returns the new offset relative to the start of the // file and an error, if any. // // Seeking to an offset before the start of the file is an error. // Seeking to any positive offset is legal, but the behavior of subsequent // I/O operations on the underlying object is implementation-dependent. type Seeker interface { Seek(offset int64, whence int) (int64, error) }
このことは公式ドキュメントでも言及されています。
os.File
など io.Seeker
実装型の値で渡せば問題ないのですが、 他の io.Reader
で渡したい場合、明示的にコンテンツサイズを計算して渡してあげる必要があります。
具体的には
PutObjectInput
に、ContentLength
を設定するPutObject
第三実引数に、SwapComputePayloadSHA256ForUnsignedPayloadMiddleware
を渡したWithAPIOptions()
呼び出しを設定する
とします。
input := s3.PutObjectInput{ Bucket: aws.String(bucketName), Key: aws.String(key), Body: bytes.NewBuffer(b), ContentLength: int64(len(b)), // 1. } resp, err := client.PutObject(ctx, input, s3.WithAPIOptions( v4.SwapComputePayloadSHA256ForUnsignedPayloadMiddleware, // 2. )) if err != nil { return err }
または、下記のようにUpload Managerを使用するとペイロードハッシュのことを考慮せずとも io.Reader
を渡せます。
ただしこちらの方法を使用すると、PutObject
APIではなくマルチパートアップロードが実行されるので注意です。
uploader := manager.NewUploader(client) _, err = uploader.Upload(ctx, &s3.PutObjectInput{ Bucket: aws.String(bucketName), Key: aws.String(key), Body: bytes.NewBuffer(b), })
module version
github.com/aws/aws-sdk-go-v2 v1.11.1 github.com/aws/aws-sdk-go-v2/config v1.10.2 github.com/aws/aws-sdk-go-v2/credentials v1.6.2 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.7.2 github.com/aws/aws-sdk-go-v2/service/s3 v1.19.1