View a markdown version of this page

Amazon S3 ユーティリティ - AWS SDK for Go v2

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

Amazon S3 ユーティリティ

Amazon S3 転送マネージャー

Amazon S3 のアップロードおよびダウンロードマネージャーは、大きなオブジェクトを複数のパートに分割して並列で転送できます。これにより、中断された転送を簡単に再開できます。

Amazon S3 アップロードマネージャー

Amazon S3 アップロードマネージャーは、ファイルを小さなパートに分割可能かどうかを判断し、可能な場合は並列でアップロードします。並列アップロードの数とアップロードされるパートのサイズをカスタマイズできます。

次の例では、Amazon S3 Uploader を使用してファイルをアップロードします。Uploader の使用は s3.PutObject() オペレーションと似ています。

import "context" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" import "github.com/aws/aws-sdk-go-v2/feature/s3/manager" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := s3.NewFromConfig(cfg) uploader := manager.NewUploader(client) result, err := uploader.Upload(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("my-object-key"), Body: uploadFile, })

設定オプション

NewUploader を使用して Uploader インスタンスを作成する際に、アップロード方法をカスタマイズするための設定オプションを指定できます。オプションは、NewUploader に 1 つ以上の引数を渡すことでオーバーライドされます。オプションには以下が含まれます:

  • PartSize – アップロードする各パートのバッファサイズをバイト単位で指定します。パートごとの最小サイズは 5 MiB です。

  • Concurrency – 並行でアップロードするパートの数を指定します。

  • LeavePartsOnError – Amazon S3 に正常にアップロードされたパートを残すかどうかを指定します。

Concurrency の値は、1 回の Upload 呼び出しで可能なパートアップロードの同時実行数の上限を制御します。これはグローバルなクライアント同時実行数の上限ではありません。PartSizeConcurrency の設定値を調整して、最適な設定を見つけることができます。例えば、高帯域幅の接続を備えたシステムでは、送信パートサイズを大きくしたり、並列アップロード数を増やしたりできます。

例えば、アプリケーションで UploaderConcurrency5 に設定したとします。そのアプリケーションが異なる 2 つの goroutine から Upload を呼び出すと、結果は 10 の同時パートアップロード (2 goroutine × 5 Concurrency) になります。

警告

アプリケーションはリソースの枯渇を防ぐために、Upload の同時呼び出し数を制限することが求められます。

次に示しているのは、Uploader 作成時にデフォルトのパートサイズを設定する例です。

uploader := manager.NewUploader(client, func(u *Uploader) { u.PartSize = 10 * 1024 * 1024, // 10 MiB })

Uploader とその設定の詳細については、 AWS SDK for Go API リファレンスの「アップローダー」を参照してください。

PutObjectInput 本文フィールド (io.ReadSeeker と io.Reader の比較)

s3.PutObjectInput 構造体の Body フィールドは io.Reader 型です。ただし、ホスト環境でのリソース使用率を最適化するために、このフィールドには、io.ReadSeekerio.ReaderAt の両方のインターフェイスを実装した型を指定できます。次の例では、両方のインターフェイスを実装した ReadSeekerAt 型を作成しています。

type ReadSeekerAt interface { io.ReadSeeker io.ReaderAt }

io.Reader 型の場合、パートをアップロードする前にリーダーのバイト列をメモリにバッファする必要があります。PartSize または Concurrency の値を増やすと、Uploader に必要なメモリ (RAM) が大幅に増加します。必要なメモリはおよそ PartSize × Concurrency です。例えば、PartSize に 100 MB、Concurrency に 10 を指定した場合、1 GB 以上が必要になります。

io.Reader 型では、バイトを読み込む前にサイズを判別できないため、Uploader はアップロードするパートの数を計算できません。その結果、PartSize を小さく設定しすぎると、大きなファイルの場合に、Uploader は Amazon S3 のアップロード上限である 10,000 パートに達する可能性があります。10,000 パートを超えてアップロードしようとすると、アップロードは停止し、エラーが返されます。

ReadSeekerAt 型を実装した body 値の場合、Uploader は本文の内容をメモリにバッファせずに Amazon S3 に送信します。Uploader は必要なパート数を計算してから、ファイルを Amazon S3 にアップロードします。PartSize の現在の値でファイルのアップロードに 10,000 を超えるパートが必要になる場合、Uploader はパートサイズの値を増やして、必要なパート数を減らします。

アップロード失敗時の処理

Amazon S3 へのアップロードが失敗した場合、デフォルトでは Uploader が Amazon S3 の AbortMultipartUpload オペレーションを使用して、アップロードされたパートを削除します。この機能により、失敗したアップロードが Amazon S3 のストレージを無駄に消費しないようにできます。

LeavePartsOnError を true に設定することで、Uploader が正常にアップロードされたパートを削除しないようにできます。これは、部分的に完了したアップロードを再開する際に便利です。アップロードされたパートを操作するには、失敗したアップロードの UploadID を取得する必要があります。次の例では、manager.MultiUploadFailure エラーインターフェイス型を使用して UploadID を取得する方法を示しています。

result, err := uploader.Upload(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("my-object-key"), Body: uploadFile, }) output, err := u.upload(input) if err != nil { var mu manager.MultiUploadFailure if errors.As(err, &mu) { // Process error and its associated uploadID fmt.Println("Error:", mu) _ = mu.UploadID() // retrieve the associated UploadID } else { // Process error generically fmt.Println("Error:", err.Error()) } return }

アップロードごとの Uploader オプションのオーバーライド

Upload を呼び出す際に、1 つ以上の引数を渡すことで、Uploader オプションをオーバーライドできます。これらのオーバーライドは同時実行環境でも安全に実行でき、現在進行中のアップロードやその後の Upload 呼び出しには影響しません。例えば、特定のアップロードリクエストに対して PartSize 設定をオーバーライドするには、次のようにします。

params := &s3.PutObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("my-key"), Body: myBody, } resp, err := uploader.Upload(context.TODO(), params, func(u *manager.Uploader) { u.PartSize = 10 * 1024 * 1024, // 10 MiB })

Amazon S3 にフォルダをアップロードする

次の例では、path/filepath パッケージを使用してファイルのリストを再帰的に収集し、指定した Amazon S3 バケットにアップロードしています。Amazon S3 オブジェクトのキーには、ファイルの相対パスがプレフィックスとして付加されます。

package main import ( "context" "log" "os" "path/filepath" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/s3" ) var ( localPath string bucket string prefix string ) func init() { if len(os.Args) != 4 { log.Fatalln("Usage:", os.Args[0], "<local path> <bucket> <prefix>") } localPath = os.Args[1] bucket = os.Args[2] prefix = os.Args[3] } func main() { walker := make(fileWalk) go func() { // Gather the files to upload by walking the path recursively if err := filepath.Walk(localPath, walker.Walk); err != nil { log.Fatalln("Walk failed:", err) } close(walker) }() cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Fatalln("error:", err) } // For each file found walking, upload it to Amazon S3 uploader := manager.NewUploader(s3.NewFromConfig(cfg)) for path := range walker { rel, err := filepath.Rel(localPath, path) if err != nil { log.Fatalln("Unable to get relative path:", path, err) } file, err := os.Open(path) if err != nil { log.Println("Failed opening file", path, err) continue } defer file.Close() result, err := uploader.Upload(context.TODO(), &s3.PutObjectInput{ Bucket: &bucket, Key: aws.String(filepath.Join(prefix, rel)), Body: file, }) if err != nil { log.Fatalln("Failed to upload", path, err) } log.Println("Uploaded", path, result.Location) } } type fileWalk chan string func (f fileWalk) Walk(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { f <- path } return nil }

ダウンロードマネージャー

Amazon S3 Downloader マネージャーは、ファイルを小さなパートに分割可能かどうかを判断し、可能な場合は並列でダウンロードします。並列ダウンロードの数とダウンロードされるパートのサイズをカスタマイズできます。

例: ファイルをダウンロードする

次の例では、Amazon S3 Downloader を使用してファイルをダウンロードしています。Downloader の使用は s3.GetObject オペレーションと似ています。

import "context" import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" import "github.com/aws/aws-sdk-go-v2/feature/s3/manager" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Println("error:", err) return } client := s3.NewFromConfig(cfg) downloader := manager.NewDownloader(client) numBytes, err := downloader.Download(context.TODO(), downloadFile, &s3.GetObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("my-key"), })

downloadFile パラメータは io.WriterAt 型です。WriterAt インターフェイスを使用して、Downloader はファイルの複数のパートを並列で書き込むことができます。

設定オプション

Downloader インスタンスを作成する際、設定オプションを指定して、オブジェクトのダウンロード方法をカスタマイズできます。

  • PartSize – ダウンロードする各パートのバッファサイズをバイト単位で指定します。パートごとの最小サイズは 5 MB です。

  • Concurrency – 並行でダウンロードするパートの数を指定します。

Concurrency の値は、1 回の Download 呼び出しで可能なパートダウンロードの同時実行数の上限を制御します。これはグローバルなクライアント同時実行数の上限ではありません。PartSizeConcurrency の設定値を調整して、最適な設定を見つけることができます。例えば、高帯域幅の接続を備えたシステムでは、受信パートサイズを大きくしたり、並列ダウンロード数を増やしたりできます。

例えば、アプリケーションで DownloaderConcurrency5 に設定したとします。そのアプリケーションが異なる 2 つの goroutine から Download を呼び出すと、結果は 10 の同時パートダウンロード (2 goroutine × 5 Concurrency) になります。

警告

アプリケーションはリソースの枯渇を防ぐために、Download の同時呼び出し数を制限することが求められます。

Downloader とそのその他の設定オプションの詳細については、 AWS SDK for Go API リファレンスの「manage.Downloader」を参照してください。

ダウンロードごとの Downloader オプションのオーバーライド

Download を呼び出すときに、1 つ以上の関数型引数を渡すことで、Downloader オプションをオーバーライドできます。これらのオーバーライドは同時実行環境でも安全に実行でき、現在進行中のダウンロードやその後の Download 呼び出しには影響しません。例えば、特定のアップロードリクエストに対して PartSize 設定をオーバーライドするには、次のようにします。

params := &s3.GetObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("my-key"), } resp, err := downloader.Download(context.TODO(), targetWriter, params, func(u *manager.Downloader) { u.PartSize = 10 * 1024 * 1024, // 10 MiB })
バケット内のすべてのオブジェクトをダウンロードする

次の例では、ページ分割を使用して Amazon S3 バケットからオブジェクトのリストを収集しています。その後、各オブジェクトをローカルファイルにダウンロードしています。

package main import ( "context" "fmt" "log" "os" "path/filepath" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/s3" ) var ( Bucket = "amzn-s3-demo-bucket" // Download from this bucket Prefix = "logs/" // Using this key prefix LocalDirectory = "s3logs" // Into this directory ) func main() { cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Fatalln("error:", err) } client := s3.NewFromConfig(cfg) manager := manager.NewDownloader(client) paginator := s3.NewListObjectsV2Paginator(client, &s3.ListObjectsV2Input{ Bucket: &Bucket, Prefix: &Prefix, }) for paginator.HasMorePages() { page, err := paginator.NextPage(context.TODO()) if err != nil { log.Fatalln("error:", err) } for _, obj := range page.Contents { if err := downloadToFile(manager, LocalDirectory, Bucket, aws.ToString(obj.Key)); err != nil { log.Fatalln("error:", err) } } } } func downloadToFile(downloader *manager.Downloader, targetDirectory, bucket, key string) error { // Create the directories in the path file := filepath.Join(targetDirectory, key) if err := os.MkdirAll(filepath.Dir(file), 0775); err != nil { return err } // Set up the local file fd, err := os.Create(file) if err != nil { return err } defer fd.Close() // Download the file using the AWS SDK for Go fmt.Printf("Downloading s3://%s/%s to %s...\n", bucket, key, file) _, err = downloader.Download(context.TODO(), fd, &s3.GetObjectInput{Bucket: &bucket, Key: &key}) return err }

GetBucketRegion

GetBucketRegion は、Amazon S3 バケットの AWS リージョンの場所を決定するためのユーティリティ関数です。 GetBucketRegion は Amazon S3 クライアントを取得し、それを使用して、クライアントの設定済みリージョンに関連付けられた AWS パーティション内のリクエストされたバケットの場所を決定します。

例えば、バケット amzn-s3-demo-bucket のリージョンを見つけるには、次のようにします。

cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Println("error:", err) return } bucket := "amzn-s3-demo-bucket" region, err := manager.GetBucketRegion(ctx, s3.NewFromConfig(cfg), bucket) if err != nil { var bnf manager.BucketNotFound if errors.As(err, &bnf) { log.Printf("unable to find bucket %s's Region\n", bucket) } else { log.Println("error:", err) } return } fmt.Printf("Bucket %s is in %s region\n", bucket, region)

GetBucketRegion がバケットの場所を解決できない場合、例に示すように、この関数は BucketNotFound エラー型を返します。

シーク不可能なストリーミング入力

PutObjectUploadPart などの API オペレーションでは、Amazon S3 クライアントは、入力パラメータ Body の値がデフォルトで io.Seeker インターフェイスを実装していることを前提としています。io.Seeker インターフェイスは、クライアントがアップロードする値の長さを判断したり、リクエスト署名のためのペイロードハッシュを計算したりするために使用されます。Body 入力パラメータ値が io.Seeker を実装していない場合、アプリケーションにはエラーが返されます。

operation error S3: PutObject, failed to compute payload hash: failed to seek body to start, request stream is not seekable

この動作は、オペレーションメソッドの ミドルウェア を関数型オプションを使用して変更することで制御できます。WithAPIOptions ヘルパーは、0 個以上のミドルウェアミューテーター用の関数型オプションを返します。クライアントによるペイロードハッシュの計算を無効にし、署名なしペイロードリクエスト署名を使用するには、v4.SwapComputePayloadSHA256ForUnsignedPayloadMiddleware を追加します。

resp, err := client.PutObject(context.TODO(), &s3.PutObjectInput{ Bucket: &bucketName, Key: &objectName, Body: bytes.NewBuffer([]byte(`example object!`)), ContentLength: 15, // length of body }, s3.WithAPIOptions( v4.SwapComputePayloadSHA256ForUnsignedPayloadMiddleware, ))
警告

Amazon S3 では、オブジェクトをバケットにアップロードするすべてのオペレーションで、ContentLength を指定する必要があります。Body 入力パラメータが io.Seeker インターフェイスを実装していないため、クライアントはリクエストに必要な ContentLength パラメータを計算できません。このパラメータはアプリケーションが指定する必要があります。ContentLength パラメータが指定されていない場合、リクエストは失敗します。

シーク不可能で長さが不明なアップロードには、SDK の Amazon S3 アップロードマネージャー を使用してください。