

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# Amazon S3 公用程式
<a name="sdk-utilities-s3"></a>

## Amazon S3 Transfer Manager
<a name="transfer-managers"></a>

 Amazon S3 上傳和下載管理員可以分解大型物件，因此可以以多個部分平行傳輸。這可讓您輕鬆恢復中斷的傳輸。

### Amazon S3 上傳管理員
<a name="s3-upload-manager"></a>

 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,
})
```

#### 組態選項
<a name="configuration-options"></a>

 當您使用 [NewUploader](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/feature/s3/manager#NewUploader) 執行個體化`Uploader`執行個體時，您可以指定數個組態選項來自訂物件的上傳方式。選項是透過提供一或多個引數給 來覆寫`NewUploader`。這些選項包括：
+  `PartSize` – 指定要上傳之每個部分的緩衝區大小，以位元組為單位。每個部分的最小大小為 5 MiB。
+  `Concurrency` – 指定要平行上傳的組件數量。
+  `LeavePartsOnError` – 指出是否要在 Amazon S3 中保留成功上傳的組件。

 `Concurrency` 值會限制特定`Upload`呼叫可能發生的並行部分上傳數量。這不是全域用戶端並行限制。調整 `PartSize`和 `Concurrency`組態值以尋找最佳組態。例如，具有高頻寬連線的系統可以平行傳送更大的部分和更多的上傳。

 例如，您的應用程式`Uploader`使用 `Concurrency`的 設定 `5`。如果您的應用程式接著`Upload`從兩個不同的 goroutine 呼叫 ，則結果為`10`並行部分上傳 (2 個 goroutines \$1 5)`Concurrency`。

**警告**  
 預期您的應用程式會將並行呼叫限制為 `Upload` ，以防止應用程式資源耗盡。

 以下是在`Uploader`建立期間設定預設組件大小的範例：

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

 如需 `Uploader`及其組態的詳細資訊，請參閱 適用於 Go 的 AWS SDK API 參考中的[上傳者](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/feature/s3/manager#Uploader)。

#### PutObjectInput 內文欄位 (io.ReadSeeker 與 io.Reader)
<a name="putobjectinput-body-field-ioreadseeker-vs-ioreader"></a>

 `s3.PutObjectInput` 結構的 `Body` 欄位是 `io.Reader`類型。不過，此欄位可以填入同時滿足 `io.ReadSeeker`和 `io.ReaderAt` 界面的類型，以改善主機環境的應用程式資源使用率。下列範例會建立滿足兩個界面`ReadSeekerAt`的類型：

```
type ReadSeekerAt interface {
    io.ReadSeeker
    io.ReaderAt
}
```

 對於 `io.Reader`類型，讀取器的位元組必須在記憶體中緩衝，才能上傳組件。當您增加 `PartSize`或 `Concurrency`值時， 所需的記憶體 (RAM) 會大幅`Uploader`增加。所需的記憶體約為 *`PartSize`* \$1 *`Concurrency`*。例如，為 指定 100 MB，為 `PartSize` 指定 10`Concurrency`， 至少需要 1 GB。

 由於 `io.Reader`類型無法在讀取其位元組之前判斷其大小，因此 `Uploader`無法計算上傳的組件數量。因此，如果您設定`PartSize`過低， `Uploader`可以達到大型檔案 10，000 個部分的 Amazon S3 上傳限制。如果您嘗試上傳超過 10，000 個組件，上傳會停止並傳回錯誤。

 對於實作 `ReadSeekerAt`類型的`body`值， `Uploader`不會在傳送至 Amazon S3 之前緩衝記憶體中的內文內容。 會在將檔案上傳至 Amazon S3 之前`Uploader`計算預期的組件數量。如果目前的 值`PartSize`需要超過 10，000 個組件才能上傳檔案， `Uploader`會增加組件大小值，因此需要較少的組件。

#### 處理失敗的上傳
<a name="handling-failed-uploads"></a>

 如果上傳至 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
}
```

#### 每次上傳覆寫上傳者選項
<a name="overriding-uploader-options-per-upload"></a>

 您可以透過為 方法提供一或多個引數`Upload`來覆寫呼叫時`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
})
```

#### 範例
<a name="examples"></a>

##### 將資料夾上傳至 Amazon S3
<a name="upload-a-folder-to-s3"></a>

 下列範例使用 `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
}
```

### Download Manager
<a name="download-manager"></a>

 Amazon S3 [Downloader](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/feature/s3/manager#Downloader) Manager 會判斷檔案是否可以分割成較小的部分並平行下載。您可以自訂平行下載的數量，以及下載組件的大小。

#### 範例：下載檔案
<a name="example-download-a-file"></a>

 下列範例使用 Amazon S3 `Downloader`下載檔案。使用 `Downloader` 類似於 [s3.GetObject ](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3#Client.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`寫入檔案的多個部分。

#### 組態選項
<a name="configuration-options-1"></a>

 當您執行個體執行個體化時`Downloader`，您可以指定組態選項來自訂物件的下載方式：
+  `PartSize` – 指定要下載的每個部分的緩衝區大小，以位元組為單位。每個組件的大小下限為 5 MB。
+  `Concurrency` – 指定要平行下載的組件數量。

 `Concurrency` 值會限制特定`Download`呼叫可能發生的並行部分下載數量。這不是全域用戶端並行限制。調整 `PartSize`和 `Concurrency`組態值以尋找最佳組態。例如，具有高頻寬連線的系統可以平行接收更大的部分和更多的下載。

 例如，您的應用程式`Downloader`會使用 `Concurrency`的 來設定 `5`。然後，您的應用程式`Download`會從兩個不同的 goroutine 呼叫 ，結果將是`10`並行部分下載 (2 個 goroutines \$1 5)`Concurrency`。

**警告**  
 預期您的應用程式會將並行呼叫限制為 `Download` ，以防止應用程式資源耗盡。

 如需 `Downloader` 及其其他組態選項的詳細資訊，請參閱 適用於 Go 的 AWS SDK API 參考中的 [manager.Downloader](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/feature/s3/manager/#Downloader)。

#### 每次下載覆寫下載程式選項
<a name="overriding-downloader-options-per-download"></a>

 您可以透過為 方法提供一或多個功能引數`Download`來覆寫呼叫時`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
})
```

##### 範例
<a name="examples-1"></a>

##### 下載儲存貯體中的所有物件
<a name="download-all-objects-in-a-bucket"></a>

 下列範例使用分頁從 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
<a name="getbucketregion"></a>

 [GetBucketRegion](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/feature/s3/manager#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](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/feature/s3/manager#BucketNotFound) 錯誤類型，如範例所示。

## 不可查看的串流輸入
<a name="unseekable-streaming-input"></a>

 對於 `PutObject`和 等 API 操作`UploadPart`，Amazon S3 用戶端預期`Body`輸入參數的值預設會實作 [io.Seeker](https://pkg.go.dev/io#Seeker) 介面。用戶端會使用 `io.Seeker` 介面來判斷要上傳的值長度，以及計算[請求簽章](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html)的承載雜湊。如果`Body`輸入參數值未實作 `io.Seeker`，您的應用程式將收到錯誤。

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

 您可以使用[中介軟體](middleware.md)功能選項修改操作方法的 ，以變更此行為。[WithAPIOptions](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3#WithAPIOptions) 協助程式會傳回零或多個中介軟體變動器的功能選項。若要停用運算承載雜湊的用戶端並使用[未簽署承載](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html)請求簽章，請新增 [v4.SwapComputePayloadSHA256ForUnsignedPayloadMiddleware。](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws/signer/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 要求為所有上傳至儲存貯體的物件提供內容長度。由於`Body`輸入參數未實作`io.Seeker`界面，用戶端將無法計算請求的 `ContentLength` 參數。參數必須由應用程式提供。如果未提供 `ContentLength` 參數，請求將會失敗。  
 將 SDK 的 [Amazon S3 上傳管理員](#s3-upload-manager) 用於無法尋找且沒有已知長度的上傳。