

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

# 使用 Amazon S3
<a name="examples-s3"></a>

本節提供使用 使用 Amazon S3 的背景資訊 AWS SDK for Java 2.x。本節補充本指南[程式碼範例一節中呈現的 Amazon S3 Java v2](java_s3_code_examples.md) *範例*。

## 中的 S3 用戶端 AWS SDK for Java 2.x
<a name="s3-clients"></a>

 AWS SDK for Java 2.x 提供不同類型的 S3 用戶端。下表顯示差異，並可協助您決定最適合使用案例的方案。


**Amazon S3 用戶端的不同口味**  

| S3 用戶端 | 簡短描述 | 使用情況 | 限制/撤回 | 
| --- | --- | --- | --- | 
|  **AWS CRT 型 S3 用戶端** 介面：[S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) 建置器：[S3CrtAsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html) 請參閱 [使用高效能 S3 用戶端： AWS CRT 型 S3 用戶端](crt-based-s3-client.md)。  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  | 
|  **啟用分段*的* Java 型 S3 非同步用戶端** 介面：[S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) Builder：[S3AsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html) 請參閱 [將 Java 型 S3 非同步用戶端設定為使用平行傳輸](s3-async-client-multipart.md)。  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  | 效能低於 AWS CRT 型 S3 用戶端。 | 
|  ***未*啟用分段的 Java 型 S3 非同步用戶端** 介面：[S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) Builder：[S3AsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  |  無效能最佳化。  | 
|  **Java 型 S3 同步用戶端** 介面：[S3Client](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html) Builder：[S3ClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3ClientBuilder.html) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  |  無效能最佳化。  | 

**注意**  
從 2.18.x 版及更新版本開始， AWS SDK for Java 2.x 會在包含端點覆寫時使用[虛擬託管式定址](https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#virtual-hosted-style-access)。只要儲存貯體名稱是有效的 DNS 標籤，這就適用。  
在用戶端建置器`true`中使用 呼叫 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3BaseClientBuilder.html#forcePathStyle(java.lang.Boolean](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3BaseClientBuilder.html#forcePathStyle(java.lang.Boolean)方法，強制用戶端使用儲存貯體的路徑樣式定址。  
下列範例顯示使用端點覆寫和路徑樣式定址設定的服務用戶端。  

```
S3Client client = S3Client.builder()
                          .region(Region.US_WEST_2)
                          .endpointOverride(URI.create("https://s3.us-west-2.amazonaws.com"))
                          .forcePathStyle(true)
                          .build();
```

**Topics**
+ [開發套件中的 S3 用戶端](#s3-clients)
+ [將串流上傳至 S3](best-practices-s3-uploads.md)
+ [預先簽章URLs](examples-s3-presign.md)
+ [跨區域存取](s3-cross-region.md)
+ [使用檢查總和保護資料完整性](s3-checksums.md)
+ [使用高效能 S3 用戶端](crt-based-s3-client.md)
+ [設定平行傳輸支援](s3-async-client-multipart.md)
+ [傳輸檔案和目錄](transfer-manager.md)
+ [S3 事件通知](examples-s3-event-notifications.md)

# 使用 將串流上傳至 Amazon S3 AWS SDK for Java 2.x
<a name="best-practices-s3-uploads"></a>

當您使用 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#putObject(software.amazon.awssdk.services.s3.model.PutObjectRequest,software.amazon.awssdk.core.sync.RequestBody)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#putObject(software.amazon.awssdk.services.s3.model.PutObjectRequest,software.amazon.awssdk.core.sync.RequestBody))或 使用串流將內容上傳至 S3 時[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#uploadPart(software.amazon.awssdk.services.s3.model.UploadPartRequest,software.amazon.awssdk.core.sync.RequestBody)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#uploadPart(software.amazon.awssdk.services.s3.model.UploadPartRequest,software.amazon.awssdk.core.sync.RequestBody))，您可以使用同步 API 的`RequestBody`原廠類別來提供串流。對於非同步 API， `AsyncRequestBody`是同等的原廠類別。

## 哪些方法上傳串流？
<a name="s3-stream-upload-methods"></a>

對於同步 API，您可以使用下列原廠方法來`RequestBody`提供串流：
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromInputStream(java.io.InputStream,long))(InputStream inputStream, long contentLength)`

  `[fromContentProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromContentProvider(software.amazon.awssdk.http.ContentStreamProvider,long,java.lang.String))(ContentStreamProvider provider, long contentLength, String mimeType)`
  + `ContentStreamProvider` 具有`fromInputStream(InputStream inputStream)`原廠方法
+ `[fromContentProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromContentProvider(software.amazon.awssdk.http.ContentStreamProvider,java.lang.String))(ContentStreamProvider provider, String mimeType)`

對於非同步 API，您可以使用 的下列原廠方法`AsyncRequestBody`：
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(java.io.InputStream,java.lang.Long,java.util.concurrent.ExecutorService))(InputStream inputStream, Long contentLength, ExecutorService executor)` 
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(software.amazon.awssdk.core.async.AsyncRequestBodyFromInputStreamConfiguration))(AsyncRequestBodyFromInputStreamConfiguration configuration)`
  + 您可以使用 AsyncRequestBodyFromInputStreamConfiguration.Builder 來提供串流
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(java.util.function.Consumer))(Consumer<AsyncRequestBodyFromInputStreamConfiguration.Builder> configuration)`
+ `[forBlockingInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#forBlockingInputStream(java.lang.Long))(Long contentLength)`
  + 產生的 `[BlockingInputStreamAsyncRequestBody](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/BlockingInputStreamAsyncRequestBody.html)`包含`writeInputStream(InputStream inputStream)`您可以用來提供串流的方法

## 執行上傳
<a name="s3-upload-stream-perform"></a>

### 如果您知道串流的長度
<a name="s3-stream-upload-supply-content-length"></a>

如先前所示方法的簽章所示，大多數方法都接受內容長度參數。

如果您知道以位元組為單位的內容長度，請提供確切的值：

```
// Always provide the exact content length when it's available.
long contentLength = 1024; // Exact size in bytes.
s3Client.putObject(req -> req
    .bucket("amzn-s3-demo-bucket")
    .key("my-key"),
RequestBody.fromInputStream(inputStream, contentLength));
```

**警告**  
 當您從輸入串流上傳時，如果您指定的內容長度不符合實際的位元組計數，您可能會遇到：  
如果您指定的長度太小，則會截斷物件
如果您指定的長度太大，上傳或掛起連線失敗

### 如果您不知道串流的長度
<a name="s3-stream-upload-unknown-length"></a>

#### 使用同步 API
<a name="s3-upload-unknown-sync-client"></a>

使用 `fromContentProvider(ContentStreamProvider provider, String mimeType)`：

```
public PutObjectResponse syncClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3Client s3Client = S3Client.create();

    RequestBody body = RequestBody.fromContentProvider(ContentStreamProvider.fromInputStream(inputStream), "text/plain");
    PutObjectResponse putObjectResponse = s3Client.putObject(b -> b.bucket(BUCKET_NAME).key(KEY_NAME), body);
    return putObjectResponse;
}
```

由於 SDK 會在記憶體中緩衝整個串流以計算內容長度，因此您可以使用大型串流遇到記憶體問題。如果您需要使用同步用戶端上傳大型串流，請考慮使用分段 API：

##### 使用同步用戶端 API 和分段 API 上傳串流
<a name="sync-multipart-upload-stream"></a>

```
public static void uploadStreamToS3(String bucketName, String key, InputStream inputStream) {
    // Create S3 client
    S3Client s3Client = S3Client.create();
    try {
        // Step 1: Initiate the multipart upload
        CreateMultipartUploadRequest createMultipartUploadRequest = CreateMultipartUploadRequest.builder()
                .bucket(bucketName)
                .key(key)
                .build();

        CreateMultipartUploadResponse createResponse = s3Client.createMultipartUpload(createMultipartUploadRequest);
        String uploadId = createResponse.uploadId();
        System.out.println("Started multipart upload with ID: " + uploadId);

        // Step 2: Upload parts
        List<CompletedPart> completedParts = new ArrayList<>();
        int partNumber = 1;
        byte[] buffer = new byte[PART_SIZE];
        int bytesRead;

        try {
            while ((bytesRead = readFullyOrToEnd(inputStream, buffer)) > 0) {
                // Create request to upload a part
                UploadPartRequest uploadPartRequest = UploadPartRequest.builder()
                        .bucket(bucketName)
                        .key(key)
                        .uploadId(uploadId)
                        .partNumber(partNumber)
                        .build();

                // If we didn't read a full buffer, create a properly sized byte array
                RequestBody requestBody;
                if (bytesRead < PART_SIZE) {
                    byte[] lastPartBuffer = new byte[bytesRead];
                    System.arraycopy(buffer, 0, lastPartBuffer, 0, bytesRead);
                    requestBody = RequestBody.fromBytes(lastPartBuffer);
                } else {
                    requestBody = RequestBody.fromBytes(buffer);
                }

                // Upload the part and save the response's ETag
                UploadPartResponse uploadPartResponse = s3Client.uploadPart(uploadPartRequest, requestBody);
                CompletedPart part = CompletedPart.builder()
                        .partNumber(partNumber)
                        .eTag(uploadPartResponse.eTag())
                        .build();
                completedParts.add(part);

                System.out.println("Uploaded part " + partNumber + " with size " + bytesRead + " bytes");
                partNumber++;
            }

            // Step 3: Complete the multipart upload
            CompletedMultipartUpload completedMultipartUpload = CompletedMultipartUpload.builder()
                    .parts(completedParts)
                    .build();

            CompleteMultipartUploadRequest completeRequest = CompleteMultipartUploadRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .multipartUpload(completedMultipartUpload)
                    .build();

            CompleteMultipartUploadResponse completeResponse = s3Client.completeMultipartUpload(completeRequest);
            System.out.println("Multipart upload completed. Object URL: " + completeResponse.location());

        } catch (Exception e) {
            // If an error occurs, abort the multipart upload
            System.err.println("Error during multipart upload: " + e.getMessage());
            AbortMultipartUploadRequest abortRequest = AbortMultipartUploadRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .build();
            s3Client.abortMultipartUpload(abortRequest);
            System.err.println("Multipart upload aborted");
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                System.err.println("Error closing input stream: " + e.getMessage());
            }
        }
    } finally {
        s3Client.close();
    }
}

/**
 * Reads from the input stream into the buffer, attempting to fill the buffer completely
 * or until the end of the stream is reached.
 *
 * @param inputStream the input stream to read from
 * @param buffer      the buffer to fill
 * @return the number of bytes read, or -1 if the end of the stream is reached before any bytes are read
 * @throws IOException if an I/O error occurs
 */
private static int readFullyOrToEnd(InputStream inputStream, byte[] buffer) throws IOException {
    int totalBytesRead = 0;
    int bytesRead;

    while (totalBytesRead < buffer.length) {
        bytesRead = inputStream.read(buffer, totalBytesRead, buffer.length - totalBytesRead);
        if (bytesRead == -1) {
            break; // End of stream
        }
        totalBytesRead += bytesRead;
    }

    return totalBytesRead > 0 ? totalBytesRead : -1;
}
```

**注意**  
對於大多數使用案例，我們建議針對大小不明的串流使用非同步用戶端 API。這種方法可以實現平行傳輸並提供更簡單的程式設計界面，因為如果串流很大，開發套件會將串流分割處理為分段區塊。  
啟用分段的標準 S3 非同步用戶端和 CRT 型 S3 AWS 用戶端都會實作此方法。我們在下一節中顯示此方法的範例。

#### 使用非同步 API
<a name="s3-stream-upload-unknown-async-client"></a>

您可以將 `contentLength`引數`null`提供給 `fromInputStream(InputStream inputStream, Long contentLength, ExecutorService executor)`

**Example 使用以 AWS CRT 為基礎的非同步用戶端：**  

```
public PutObjectResponse crtClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor);  // 'null' indicates that the
                                                                                            // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null){
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

**Example 使用啟用分段的標準非同步用戶端：**  

```
public PutObjectResponse asyncClient_multipart_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.builder().multipartEnabled(true).build();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor); // 'null' indicates that the
                                                                                           // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null) {
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

# 使用 Amazon S3 預先簽章URLs
<a name="examples-s3-presign"></a>

預先簽章URLs 提供私有 S3 物件的暫時存取權，而不需要使用者擁有 AWS 登入資料或許可。

例如，假設 Alice 可以存取 S3 物件，而且她想要暫時與 Bob 共用該物件的存取權。Alice 可以產生預先簽章的 GET 請求來與 Bob 共用，以便他可以下載物件，而無需存取 Alice 的登入資料。您可以為 HTTP GET 產生預先簽章URLs，並為 HTTP PUT 請求產生預先簽章的 URL。

## 產生物件的預先簽章 URL，然後下載它 (GET 請求）
<a name="get-presignedobject"></a>

下列範例包含兩個部分。
+ 第 1 部分：Alice 會產生物件的預先簽章 URL。
+ 第 2 部分：Bob 使用預先簽章的 URL 下載物件。

### 第 1 部分：產生 URL
<a name="get-presigned-object-part1"></a>

Alice 在 S3 儲存貯體中已有物件。她使用以下程式碼來產生可在後續 GET 請求中使用的 URL 字串。

#### 匯入
<a name="get-presigned-example-imports"></a>

```
import com.example.s3.util.PresignUrlUtils;
import org.slf4j.Logger;
import software.amazon.awssdk.http.HttpExecuteRequest;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
import software.amazon.awssdk.utils.IoUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.UUID;
```

```
    /* Create a pre-signed URL to download an object in a subsequent GET request. */
    public String createPresignedGetUrl(String bucketName, String keyName) {
        try (S3Presigner presigner = S3Presigner.create()) {

            GetObjectRequest objectRequest = GetObjectRequest.builder()
                    .bucket(bucketName)
                    .key(keyName)
                    .build();

            GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofMinutes(10))  // The URL will expire in 10 minutes.
                    .getObjectRequest(objectRequest)
                    .build();

            PresignedGetObjectRequest presignedRequest = presigner.presignGetObject(presignRequest);
            logger.info("Presigned URL: [{}]", presignedRequest.url().toString());
            logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

            return presignedRequest.url().toExternalForm();
        }
    }
```

### 第 2 部分：下載 物件
<a name="get-presigned-object-part2"></a>

Bob 使用下列三個程式碼選項之一來下載物件。或者，他可以使用瀏覽器來執行 GET 請求。

#### 使用 JDK `HttpURLConnection`（自 1.1 版起）
<a name="get-presigned-example-useHttpUrlConnection"></a>

```
    /* Use the JDK HttpURLConnection (since v1.1) class to do the download. */
    public byte[] useHttpUrlConnectionToGet(String presignedUrlString) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.

        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection();
            connection.setRequestMethod("GET");
            // Download the result of executing the request.
            try (InputStream content = connection.getInputStream()) {
                IoUtils.copy(content, byteArrayOutputStream);
            }
            logger.info("HTTP response code is " + connection.getResponseCode());

        } catch (S3Exception | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

#### 使用 JDK `HttpClient`（自 v11 起）
<a name="get-presigned-example-useHttpClient"></a>

```
    /* Use the JDK HttpClient (since v11) class to do the download. */
    public byte[] useHttpClientToGet(String presignedUrlString) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.

        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
        HttpClient httpClient = HttpClient.newHttpClient();
        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpResponse<InputStream> response = httpClient.send(requestBuilder
                            .uri(presignedUrl.toURI())
                            .GET()
                            .build(),
                    HttpResponse.BodyHandlers.ofInputStream());

            IoUtils.copy(response.body(), byteArrayOutputStream);

            logger.info("HTTP response code is " + response.statusCode());

        } catch (URISyntaxException | InterruptedException | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

#### `SdkHttpClient` 從適用於 Java 的 開發套件使用
<a name="get-presigned-example-useSdkHttpClient"></a>

```
    /* Use the AWS SDK for Java SdkHttpClient class to do the download. */
    public byte[] useSdkHttpClientToGet(String presignedUrlString) {

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.
        try {
            URL presignedUrl = new URL(presignedUrlString);
            SdkHttpRequest request = SdkHttpRequest.builder()
                    .method(SdkHttpMethod.GET)
                    .uri(presignedUrl.toURI())
                    .build();

            HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
                    .request(request)
                    .build();

            try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) {
                HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call();
                response.responseBody().ifPresentOrElse(
                        abortableInputStream -> {
                            try {
                                IoUtils.copy(abortableInputStream, byteArrayOutputStream);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        },
                        () -> logger.error("No response body."));

                logger.info("HTTP Response code is {}", response.httpResponse().statusCode());
            }
        } catch (URISyntaxException | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedGetUrlAndRetrieve.java)和[測試](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedGetUrlTests.java)。

## 產生上傳的預先簽章 URL，然後上傳檔案 (PUT 請求）
<a name="put-presignedobject"></a>

下列範例包含兩個部分。
+ 第 1 部分：Alice 會產生預先簽章的 URL 來上傳物件。
+ 第 2 部分：Bob 使用預先簽章的 URL 上傳檔案。

### 第 1 部分：產生 URL
<a name="put-presigned-object-part1"></a>

Alice 已有 S3 儲存貯體。她使用以下程式碼來產生可在後續 PUT 請求中使用的 URL 字串。

#### 匯入
<a name="put-presigned-example-imports"></a>

```
import com.example.s3.util.PresignUrlUtils;
import org.slf4j.Logger;
import software.amazon.awssdk.core.internal.sync.FileContentStreamProvider;
import software.amazon.awssdk.http.HttpExecuteRequest;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Map;
import java.util.UUID;
```

```
    /* Create a presigned URL to use in a subsequent PUT request */
    public String createPresignedUrl(String bucketName, String keyName, Map<String, String> metadata) {
        try (S3Presigner presigner = S3Presigner.create()) {

            PutObjectRequest objectRequest = PutObjectRequest.builder()
                    .bucket(bucketName)
                    .key(keyName)
                    .metadata(metadata)
                    .build();

            PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofMinutes(10))  // The URL expires in 10 minutes.
                    .putObjectRequest(objectRequest)
                    .build();


            PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest);
            String myURL = presignedRequest.url().toString();
            logger.info("Presigned URL to upload a file to: [{}]", myURL);
            logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

            return presignedRequest.url().toExternalForm();
        }
    }
```

### 第 2 部分：上傳檔案物件
<a name="put-presigned-object-part2"></a>

Bob 使用下列三個程式碼選項之一來上傳檔案。

#### 使用 JDK `HttpURLConnection`（自 1.1 版起）
<a name="put-presigned-example-useHttpUrlConnection"></a>

```
    /* Use the JDK HttpURLConnection (since v1.1) class to do the upload. */
    public void useHttpUrlConnectionToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());
        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection();
            connection.setDoOutput(true);
            metadata.forEach((k, v) -> connection.setRequestProperty("x-amz-meta-" + k, v));
            connection.setRequestMethod("PUT");
            OutputStream out = connection.getOutputStream();

            try (RandomAccessFile file = new RandomAccessFile(fileToPut, "r");
                 FileChannel inChannel = file.getChannel()) {
                ByteBuffer buffer = ByteBuffer.allocate(8192); //Buffer size is 8k

                while (inChannel.read(buffer) > 0) {
                    buffer.flip();
                    for (int i = 0; i < buffer.limit(); i++) {
                        out.write(buffer.get());
                    }
                    buffer.clear();
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }

            out.close();
            connection.getResponseCode();
            logger.info("HTTP response code is " + connection.getResponseCode());

        } catch (S3Exception | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

#### 使用 JDK `HttpClient`（自 v11 起）
<a name="put-presigned-example-useHttpClient"></a>

```
    /* Use the JDK HttpClient (since v11) class to do the upload. */
    public void useHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());

        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
        metadata.forEach((k, v) -> requestBuilder.header("x-amz-meta-" + k, v));

        HttpClient httpClient = HttpClient.newHttpClient();
        try {
            final HttpResponse<Void> response = httpClient.send(requestBuilder
                            .uri(new URL(presignedUrlString).toURI())
                            .PUT(HttpRequest.BodyPublishers.ofFile(Path.of(fileToPut.toURI())))
                            .build(),
                    HttpResponse.BodyHandlers.discarding());

            logger.info("HTTP response code is " + response.statusCode());

        } catch (URISyntaxException | InterruptedException | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

#### `SdkHttpClient` 從適用於 Java 的 開發套件使用
<a name="put-presigned-example-useSdkHttpClient"></a>

```
    /* Use the AWS SDK for Java V2 SdkHttpClient class to do the upload. */
    public void useSdkHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());

        try {
            URL presignedUrl = new URL(presignedUrlString);

            SdkHttpRequest.Builder requestBuilder = SdkHttpRequest.builder()
                    .method(SdkHttpMethod.PUT)
                    .uri(presignedUrl.toURI());
            // Add headers
            metadata.forEach((k, v) -> requestBuilder.putHeader("x-amz-meta-" + k, v));
            // Finish building the request.
            SdkHttpRequest request = requestBuilder.build();

            HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
                    .request(request)
                    .contentStreamProvider(new FileContentStreamProvider(fileToPut.toPath()))
                    .build();

            try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) {
                HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call();
                logger.info("Response code: {}", response.httpResponse().statusCode());
            }
        } catch (URISyntaxException | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithMetadata.java)和[測試](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java)。

# Amazon S3 的跨區域存取
<a name="s3-cross-region"></a>

當您使用 Amazon Simple Storage Service (Amazon S3) 儲存貯體時，您通常知道儲存貯體 AWS 區域 的 。您使用的區域會在您建立 S3 用戶端時決定。

不過，有時您可能需要使用特定儲存貯體，但您不知道它是否位於為 S3 用戶端設定的相同區域中。

您可以使用 SDK 來啟用跨不同區域的 S3 儲存貯體存取權，而不是進行更多呼叫來判斷儲存貯體區域。

## 設定
<a name="s3-cross-region-setup"></a>

跨區域存取的支援隨 SDK `2.20.111` 版本提供。在 Maven 建置檔案中使用此版本或更新版本做為`s3`相依性，如下列程式碼片段所示。

```
<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>s3</artifactId>
  <version>2.27.21</version>
</dependency>
```

接著，當您建立 S3 用戶端時，請啟用跨區域存取，如程式碼片段所示。根據預設，不會啟用存取。

```
S3AsyncClient client = S3AsyncClient.builder()
                                    .crossRegionAccessEnabled(true)
                                    .build();
```

## 開發套件如何提供跨區域存取
<a name="s3-cross-region-routing"></a>

當您在請求中參考現有的儲存貯體時，例如當您使用 `putObject`方法時，軟體開發套件會對為用戶端設定的 區域啟動請求。

如果儲存貯體不存在於該特定區域中，則錯誤回應會包含儲存貯體所在的實際區域。開發套件接著會在第二個請求中使用正確的區域。

為了最佳化對相同儲存貯體的未來請求，開發套件會在用戶端中快取此區域映射。

## 考量事項
<a name="s3-cross-region-considerations"></a>

當您啟用跨區域儲存貯體存取時，請注意，如果儲存貯體不在用戶端設定的區域中，第一個 API 呼叫可能會導致延遲增加。不過，後續呼叫會受益於快取的區域資訊，進而改善效能。

當您啟用跨區域存取時，對儲存貯體的存取不會受到影響。使用者必須獲得授權，才能在其所在的任何區域中存取儲存貯體。

# 使用檢查總和保護資料完整性
<a name="s3-checksums"></a>

Amazon Simple Storage Service (Amazon S3) 可讓您在上傳物件時指定檢查總和。當您指定檢查總和時，它會與 物件一起存放，並且可以在下載物件時進行驗證。

當您傳輸檔案時，檢查總和可提供多一層的資料完整性。使用檢查總和，您可以透過確認收到的檔案符合原始檔案來驗證資料一致性。如需使用 Amazon S3 檢查總和的詳細資訊，請參閱 [Amazon Simple Storage Service 使用者指南](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html)，包括[支援的演算法](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#using-additional-checksums)。

您可以靈活地選擇最符合您需求的演算法，並讓 SDK 計算檢查總和。或者，您可以使用其中一個支援的演算法來提供預先計算的檢查總和值。

**注意**  
從 2.30.0 版開始 AWS SDK for Java 2.x，開發套件會自動計算上傳的`CRC32`檢查總和，以提供預設完整性保護。如果您未提供預先計算的檢查總和值，或者您未指定 SDK 應該用來計算檢查總和的演算法，則 SDK 會計算此檢查總和。  
軟體開發套件還提供全域設定，用於外部設定的資料完整性保護，您可以在軟體[AWS SDKs和工具參考指南](https://docs.aws.amazon.com/sdkref/latest/guide/feature-dataintegrity.html)中閱讀這些保護。

我們討論兩個請求階段的檢查總和：上傳物件和下載物件。

## 上傳物件
<a name="use-service-S3-checksum-upload"></a>

當您使用 `putObject`方法上傳物件並提供檢查總和演算法時，開發套件會計算指定演算法的檢查總和。

下列程式碼片段顯示使用`SHA256`檢查總和上傳物件的請求。當 SDK 傳送請求時，它會計算`SHA256`檢查總和並上傳物件。Amazon S3 透過計算檢查總和並將其與 SDK 提供的檢查總和進行比較，來驗證內容的完整性。然後，Amazon S3 會將檢查總和與 物件一起存放。

```
public void putObjectWithChecksum() {
        s3Client.putObject(b -> b
                .bucket(bucketName)
                .key(key)
                .checksumAlgorithm(ChecksumAlgorithm.SHA256),
            RequestBody.fromString("This is a test"));
}
```

如果您未隨請求提供檢查總和演算法，檢查總和行為會根據您使用的 SDK 版本而有所不同，如下表所示。

**未提供檢查總和演算法時的檢查總和行為**


| Java 開發套件版本 | 檢查總和行為 | 
| --- | --- | 
| 早於 2.30.0 | 軟體開發套件不會自動計算以 CRC 為基礎的檢查總和，並在請求中提供它。 | 
| 2.30.0 或更新版本 | SDK 使用`CRC32`演算法來計算檢查總和，並在請求中提供檢查總和。Amazon S3 透過計算自己的`CRC32`檢查總和來驗證傳輸的完整性，並將其與 SDK 提供的檢查總和進行比較。如果檢查總和相符，檢查總和會與 物件一起儲存。 | 

### 使用預先計算的檢查總和值
<a name="use-service-S3-checksum-upload-pre"></a>

隨請求提供的預先計算檢查總和值會停用 SDK 的自動運算，並改用提供的值。

下列範例顯示具有預先計算 SHA256 檢查總和的請求。

```
    public void putObjectWithPrecalculatedChecksum(String filePath) {
        String checksum = calculateChecksum(filePath, "SHA-256");

        s3Client.putObject((b -> b
                .bucket(bucketName)
                .key(key)
                .checksumSHA256(checksum)),
            RequestBody.fromFile(Paths.get(filePath)));
    }
```

如果 Amazon S3 判斷指定演算法的檢查總和值不正確，則服務會傳回錯誤回應。

### 分段上傳
<a name="use-service-S3-checksum-upload-multi"></a>

您也可以使用具有分段上傳的檢查總和。

 適用於 Java 的 SDK 2.x 提供兩個選項，以使用具有分段上傳的檢查總和。第一個選項使用 `S3TransferManager`。

下列 Transfer Manager 範例會指定上傳的 SHA1 演算法。

```
    public void multipartUploadWithChecksumTm(String filePath) {
        S3TransferManager transferManager = S3TransferManager.create();
        UploadFileRequest uploadFileRequest = UploadFileRequest.builder()
            .putObjectRequest(b -> b
                .bucket(bucketName)
                .key(key)
                .checksumAlgorithm(ChecksumAlgorithm.SHA1))
            .source(Paths.get(filePath))
            .build();
        FileUpload fileUpload = transferManager.uploadFile(uploadFileRequest);
        fileUpload.completionFuture().join();
        transferManager.close();
    }
```

如果您在使用傳輸管理員進行上傳時未提供檢查總和演算法，軟體開發套件會根據`CRC32`演算法自動計算和檢查總和。軟體開發套件會針對所有版本的軟體開發套件執行此計算。

第二個選項使用 [`S3Client` API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html) （或 [`S3AsyncClient` API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html)) 來執行分段上傳。如果您使用此方法指定檢查總和，則必須指定啟動上傳時要使用的演算法。您還必須為每一個分段請求指定演算法，並在每一個分段上傳後提供為其計算的總和檢查。

```
    public void multipartUploadWithChecksumS3Client(String filePath) {
        ChecksumAlgorithm algorithm = ChecksumAlgorithm.CRC32;

        // Initiate the multipart upload.
        CreateMultipartUploadResponse createMultipartUploadResponse = s3Client.createMultipartUpload(b -> b
            .bucket(bucketName)
            .key(key)
            .checksumAlgorithm(algorithm)); // Checksum specified on initiation.
        String uploadId = createMultipartUploadResponse.uploadId();

        // Upload the parts of the file.
        int partNumber = 1;
        List<CompletedPart> completedParts = new ArrayList<>();
        ByteBuffer bb = ByteBuffer.allocate(1024 * 1024 * 5); // 5 MB byte buffer

        try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
            long fileSize = file.length();
            long position = 0;
            while (position < fileSize) {
                file.seek(position);
                long read = file.getChannel().read(bb);

                bb.flip(); // Swap position and limit before reading from the buffer.
                UploadPartRequest uploadPartRequest = UploadPartRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .checksumAlgorithm(algorithm) // Checksum specified on each part.
                    .partNumber(partNumber)
                    .build();

                UploadPartResponse partResponse = s3Client.uploadPart(
                    uploadPartRequest,
                    RequestBody.fromByteBuffer(bb));

                CompletedPart part = CompletedPart.builder()
                    .partNumber(partNumber)
                    .checksumCRC32(partResponse.checksumCRC32()) // Provide the calculated checksum.
                    .eTag(partResponse.eTag())
                    .build();
                completedParts.add(part);

                bb.clear();
                position += read;
                partNumber++;
            }
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }

        // Complete the multipart upload.
        s3Client.completeMultipartUpload(b -> b
            .bucket(bucketName)
            .key(key)
            .uploadId(uploadId)
            .multipartUpload(CompletedMultipartUpload.builder().parts(completedParts).build()));
    }
```

[完整範例和測試的程式碼](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/PerformMultiPartUpload.java)位於 GitHub 程式碼範例儲存庫中。 [https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/test/java/com/example/s3/PerformMultiPartUploadTests.java](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/test/java/com/example/s3/PerformMultiPartUploadTests.java)

## 下載物件
<a name="use-service-S3-checksum-download"></a>

當您使用 [getObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#getObject(software.amazon.awssdk.services.s3.model.GetObjectRequest)) getObject方法下載物件時，開發套件會在當 的建置器 `checksumMode`方法`GetObjectRequest`設定為 時`ChecksumMode.ENABLED`。

以下程式碼片段中的請求會指示 SDK 透過計算檢查總和並比較值來驗證回應中的檢查總和。

```
    public GetObjectResponse getObjectWithChecksum() {
        return s3Client.getObject(b -> b
                        .bucket(bucketName)
                        .key(key)
                        .checksumMode(ChecksumMode.ENABLED))
                .response();
    }
```

**注意**  
如果物件未使用檢查總和上傳，則不會進行驗證。

## 其他檢查總和計算選項
<a name="S3-checsum-calculation-options"></a>

**注意**  
為了驗證傳輸資料的資料完整性並識別任何傳輸錯誤，我們建議使用者保留檢查總和計算選項的 SDK 預設設定。根據預設，軟體開發套件會針對許多 S3 操作新增此重要檢查，包括 `PutObject`和 `GetObject`。

不過，如果您使用 Amazon S3 需要最少的檢查總和驗證，您可以透過變更預設組態設定來停用許多檢查。

### 除非需要，否則停用自動檢查總和計算
<a name="S3-minimize-checksum-calc-global"></a>

您可以針對支援 SDK 的操作停用 SDK 的自動檢查總和計算，例如 `PutObject`和 `GetObject`。不過，某些 S3 操作需要檢查總和計算；您無法停用這些操作的檢查總和計算。

開發套件針對請求的承載和回應的承載，提供計算檢查總和的個別設定。

下列清單說明您可以用來將不同範圍的檢查總和計算降至最低的設定。
+ **所有應用程式範圍** - 透過變更環境變數中的設定或共用 AWS `config`和`credentials`檔案中的設定檔，所有應用程式都可以使用這些設定。除非在應用程式或服務用戶端範圍遭到覆寫，否則這些設定會影響所有 AWS SDK 應用程式中的所有服務用戶端。
  + 在設定檔中新增設定：

    ```
    [default]
    request_checksum_calculation = WHEN_REQUIRED
    response_checksum_validation = WHEN_REQUIRED
    ```
  + 新增環境變數：

    ```
    AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED
    AWS_RESPONSE_CHECKSUM_VALIDATION=WHEN_REQUIRED
    ```
+ **目前的應用程式範圍** - 您可以將 Java 系統屬性設定為 `aws.requestChecksumCalculation``WHEN_REQUIRED`，以限制檢查總和計算。回應的對應系統屬性為 `aws.responseChecksumValidation`。

  除非在建立服務用戶端期間遭到覆寫，否則這些設定會影響應用程式中的所有 SDK 服務用戶端。

  在應用程式開始時設定系統屬性：

  ```
  import software.amazon.awssdk.core.SdkSystemSetting;
  import software.amazon.awssdk.core.checksums.RequestChecksumCalculation;
  import software.amazon.awssdk.core.checksums.ResponseChecksumValidation;
  import software.amazon.awssdk.services.s3.S3Client;
  
  class DemoClass {
      public static void main(String[] args) {
  
          System.setProperty(SdkSystemSetting.AWS_REQUEST_CHECKSUM_CALCULATION.property(), // Resolves to "aws.requestChecksumCalculation".
                  "WHEN_REQUIRED");
          System.setProperty(SdkSystemSetting.AWS_RESPONSE_CHECKSUM_VALIDATION.property(), // Resolves to "aws.responseChecksumValidation".
                  "WHEN_REQUIRED");
  
          S3Client s3Client = S3Client.builder().build();
  
          // Use s3Client.
      }
  }
  ```
+ **單一 S3 服務用戶端範圍** - 您可以使用建置器方法，設定單一 S3 服務用戶端來計算檢查總和的最小數量：

  ```
  import software.amazon.awssdk.core.checksums.RequestChecksumCalculation;
  import software.amazon.awssdk.services.s3.S3Client;
  
  public class RequiredChecksums {
      public static void main(String[] args) {
          S3Client s3 = S3Client.builder()
                  .requestChecksumCalculation(RequestChecksumCalculation.WHEN_REQUIRED)
                  .responseChecksumValidation(ResponseChecksumValidation.WHEN_REQUIRED)
                  .build();
  
          // Use s3Client. 
      }
  // ...
  }
  ```

### 使用 `LegacyMd5Plugin`以簡化 MD5 相容性
<a name="S3-checksum-legacy-md5"></a>

除了發行 2.30.0 版的 CRC32 檢查總和行為之外，SDK 也停止計算必要操作的 MD5 檢查總和。

如果您需要 S3 操作的舊版 MD5 檢查總和行為，您可以使用 `LegacyMd5Plugin`，該 已隨 SDK 的 2.31.32 版發行。

當您需要與依賴舊版 MD5 檢查總和行為的應用程式保持相容性時，`LegacyMd5Plugin`特別有用，特別是在與第三方 S3-compatible儲存提供者搭配使用時，例如與 S3A 檔案系統連接器 (Apache Spark、Iceberg) 搭配使用時。

若要使用 `LegacyMd5Plugin`，請將其新增至 S3 用戶端建置器：

```
// For synchronous S3 client.
S3Client s3Client = S3Client.builder()
                           .addPlugin(LegacyMd5Plugin.create())
                           .build();

// For asynchronous S3 client.
S3AsyncClient asyncClient = S3AsyncClient.builder()
                                       .addPlugin(LegacyMd5Plugin.create())
                                       .build();
```

如果您想要將 MD5 檢查總和新增至需要檢查總和的操作，並想要略過為支援檢查總和但非必要的操作新增 SDK 預設檢查總和，您可以啟用`ClientBuilder`選項 `requestChecksumCalculation`和 `responseChecksumValidation`作為 `WHEN_REQUIRED`。這只會將 SDK 預設檢查總和新增至需要檢查總和的操作：

```
// Use the `LegacyMd5Plugin` with `requestChecksumCalculation` and `responseChecksumValidation` set to WHEN_REQUIRED.
S3AsyncClient asyncClient = S3AsyncClient.builder()
                                       .addPlugin(LegacyMd5Plugin.create())
                                       .requestChecksumCalculation(RequestChecksumCalculation.WHEN_REQUIRED)
                                       .responseChecksumValidation(ResponseChecksumValidation.WHEN_REQUIRED)
                                       .build();
```

此組態在使用可能無法完全支援較新檢查總和演算法，但仍需要特定操作的 MD5 檢查總和的第三方 S3-compatible儲存系統時特別有用。

# 使用高效能 S3 用戶端： AWS CRT 型 S3 用戶端
<a name="crt-based-s3-client"></a>

 AWS CRT 型 S3 用戶端，建置在[AWS 通用執行期 (CRT)](https://docs.aws.amazon.com/sdkref/latest/guide/common-runtime.html) 之上，是替代的 S3 非同步用戶端。它會自動使用 Amazon S3 的[分段上傳 API](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) 和[位元組範圍擷取](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance-guidelines.html#optimizing-performance-guidelines-get-range)，以增強的效能和可靠性在 Amazon S3) 之間來回傳輸物件。

 AWS CRT 型 S3 用戶端可在發生網路故障時改善傳輸可靠性。透過重試檔案傳輸的個別失敗部分，而不從頭重新開始傳輸，可改善可靠性。

此外，以 AWS CRT 為基礎的 S3 用戶端提供增強型連線集區和網域名稱系統 (DNS) 負載平衡，也可改善輸送量。

您可以使用 AWS CRT 型 S3 用戶端取代 SDK 的標準 S3 非同步用戶端，並立即利用其改善的輸送量。

**重要**  
 AWS CRT 型 S3 用戶端目前不支援用戶端層級或請求層級的 [SDK 指標集合](metrics.md)。

**AWS 開發套件中的 CRT 型元件**

本主題中所述的 AWS CRT 型* S3* 用戶端和 AWS CRT 型 *HTTP* 用戶端是 SDK 中的不同元件。

**AWS CRT 型 S3 用戶端**是 [S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) 界面的實作，用於使用 Amazon S3 服務。這是以 Java 為基礎的`S3AsyncClient`介面實作的替代方案，並提供多種優點。

[AWS CRT 型 HTTP 用戶端](http-configuration-crt.md)是 [SdkAsyncHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/async/SdkAsyncHttpClient.html) 界面的實作，用於一般 HTTP 通訊。這是`SdkAsyncHttpClient`介面 Netty 實作的替代方案，並提供數種優點。

雖然這兩個元件都使用[AWS 通用執行期](https://docs.aws.amazon.com/sdkref/latest/guide/common-runtime.html)的程式庫，但以 AWS CRT 為基礎的 S3 用戶端會使用 [aws-c-s3 程式庫](https://github.com/awslabs/aws-c-s3)，並支援 [S3 分段上傳 API](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) 功能。由於 AWS CRT 型 HTTP 用戶端適用於一般用途，因此不支援 S3 分段上傳 API 功能。

## 新增相依性以使用 AWS CRT 型 S3 用戶端
<a name="crt-based-s3-client-depend"></a>

若要使用 AWS CRT 型 S3 用戶端，請將下列兩個相依性新增至 Maven 專案檔案。此範例顯示要使用的最低版本。搜尋 Maven 中央儲存庫以取得最新版本的 [s3](https://central.sonatype.com/artifact/software.amazon.awssdk/s3) 和 [aws-crt](https://central.sonatype.com/artifact/software.amazon.awssdk.crt/aws-crt) 成品。

```
<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>s3</artifactId>
  <version>2.27.21</version>
</dependency>
<dependency>
  <groupId>software.amazon.awssdk.crt</groupId>
  <artifactId>aws-crt</artifactId>
  <version>0.30.11</version>
</dependency>
```

## 建立 AWS CRT 型 S3 用戶端的執行個體
<a name="crt-based-s3-client-create"></a>

 使用預設設定建立 AWS CRT 型 S3 用戶端的執行個體，如下列程式碼片段所示。

```
S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
```

若要設定用戶端，請使用 AWS CRT 用戶端建置器。您可以透過變更建置器方法，從標準 S3 非同步用戶端切換至 AWS CRT 型用戶端。

```
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;


S3AsyncClient s3AsyncClient = 
        S3AsyncClient.crtBuilder()
                     .credentialsProvider(DefaultCredentialsProvider.create())
                     .region(Region.US_WEST_2)
                     .targetThroughputInGbps(20.0)
                     .minimumPartSizeInBytes(8 * 1025 * 1024L)
                     .build();
```

**注意**  
 AWS CRT 用戶端建置器目前可能不支援標準建置器中的某些設定。呼叫 以取得標準建置器`S3AsyncClient#builder()`。

## 使用 AWS CRT 型 S3 用戶端
<a name="crt-based-s3-client-use"></a>

使用 AWS CRT 型 S3 用戶端呼叫 Amazon S3 API 操作。下列範例示範 [PutObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html#putObject(java.util.function.Consumer,software.amazon.awssdk.core.async.AsyncRequestBody)) 和 [GetObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html#getObject(java.util.function.Consumer,software.amazon.awssdk.core.async.AsyncResponseTransformer)) 操作可透過 取得 適用於 Java 的 AWS SDK。

```
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;


S3AsyncClient s3Client = S3AsyncClient.crtCreate();

// Upload a local file to Amazon S3.
PutObjectResponse putObjectResponse = 
      s3Client.putObject(req -> req.bucket(<BUCKET_NAME>)
                                   .key(<KEY_NAME>),
                        AsyncRequestBody.fromFile(Paths.get(<FILE_NAME>)))
              .join();

// Download an object from Amazon S3 to a local file.
GetObjectResponse getObjectResponse = 
      s3Client.getObject(req -> req.bucket(<BUCKET_NAME>)
                                   .key(<KEY_NAME>),
                        AsyncResponseTransformer.toFile(Paths.get(<FILE_NAME>)))
              .join();
```

## 上傳大小不明的串流
<a name="crt-stream-unknown-size"></a>

 AWS AWS CRT 型 S3 用戶端的一個重要優點是能夠有效地處理未知大小的輸入串流。當您需要從無法事先決定總大小的來源上傳資料時，這特別有用。

```
public PutObjectResponse crtClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor);  // 'null' indicates that the
                                                                                            // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null){
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

此功能有助於避免傳統上傳的常見問題，其中不正確的內容長度規格可能會導致截斷物件或上傳失敗。

## 組態限制
<a name="crt-based-s3-client-limitations"></a>

 AWS CRT 型 S3 用戶端和 Java 型 S3 非同步用戶端[提供可比較的功能](examples-s3.md#s3-clients)，而 AWS CRT 型 S3 用戶端提供效能邊緣。不過，以 AWS CRT 為基礎的 S3 用戶端缺少以 Java 為基礎的 S3 非同步用戶端所擁有的組態設定。這些設定包括：
+ *用戶端層級組態：*API 呼叫嘗試逾時、壓縮執行攔截器、指標發佈者、自訂執行屬性、自訂進階選項、自訂排程執行器服務、自訂標頭
+ *請求層級組態：*自訂簽署者、API 呼叫嘗試逾時

如需組態差異的完整清單，請參閱 API 參考。


| Java 型 S3 非同步用戶端 | AWS CRT 型 S3 用戶端 | 
| --- | --- | 
| 用戶端層級組態[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/crt-based-s3-client.html)請求層級組態[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/crt-based-s3-client.html) | 用戶端層級組態[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/crt-based-s3-client.html)請求層級組態[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/crt-based-s3-client.html) | 

# 將 Java 型 S3 非同步用戶端設定為使用平行傳輸
<a name="s3-async-client-multipart"></a>

自 2.27.5 版起，標準 Java 型 S3 非同步用戶端支援自動平行傳輸 （分段上傳和下載）。您可以在建立 Java 型 S3 非同步用戶端時設定平行傳輸的支援。

本節說明如何啟用平行傳輸，以及如何自訂組態。

## 建立 執行個體 `S3AsyncClient`
<a name="s3-async-client-multipart-create"></a>

當您在不呼叫[建置器](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html)上的任何`multipart*`方法的情況下建立`S3AsyncClient`執行個體時，不會啟用平行傳輸。下列每個陳述式都會建立 Java 型 S3 非同步用戶端，而不支援分段上傳和下載。

### 在沒有分段支援*的情況下*建立
<a name="s3-async-client-mp-off"></a>

**Example**  

```
import software.amazon.awssdk.auth.credentials.ProcessCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;


S3AsyncClient s3Client = S3AsyncClient.create();

S3AsyncClient s3Client2 = S3AsyncClient.builder().build();

S3AsyncClient s3Client3 = S3AsyncClient.builder()
        .credentialsProvider(ProcessCredentialsProvider.builder().build())
        .region(Region.EU_NORTH_1)
        .build();
```

### *使用*分段支援建立
<a name="s3-async-client-mp-on"></a>

若要使用預設設定啟用平行傳輸，請呼叫建置器`multipartEnabled`上的 並傳入，`true`如下列範例所示。

**Example**  

```
S3AsyncClient s3AsyncClient2 = S3AsyncClient.builder()
        .multipartEnabled(true)
        .build();
```

`thresholdInBytes` 和 `minimumPartSizeInBytes`設定的預設值為 8 MiB。

如果您自訂分段設定，會自動啟用平行傳輸，如下所示。

**Example**  

```
import software.amazon.awssdk.services.s3.S3AsyncClient;
import static software.amazon.awssdk.transfer.s3.SizeConstant.MB;


S3AsyncClient s3AsyncClient2 = S3AsyncClient.builder()
        .multipartConfiguration(b -> b
                .thresholdInBytes(16 * MB)
                .minimumPartSizeInBytes(10 * MB))
        .build();
```

## 上傳大小不明的串流
<a name="java-async-client-stream-unknown-size"></a>

啟用分段的 Java 型 S3 非同步用戶端可以有效地處理輸入串流，其中總大小事先未知：

```
public PutObjectResponse asyncClient_multipart_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.builder().multipartEnabled(true).build();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor); // 'null' indicates that the
                                                                                           // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null) {
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

此方法可防止手動指定不正確的內容長度時可能發生的問題，例如截斷物件或上傳失敗。

# 使用 Amazon S3 Transfer Manager 傳輸檔案和目錄
<a name="transfer-manager"></a>

Amazon S3 Transfer Manager 是 的開放原始碼、高階檔案傳輸公用程式 AWS SDK for Java 2.x。使用它在 Amazon Simple Storage Service (Amazon S3) 之間傳輸檔案和目錄。

在啟用分段的 [AWS CRT 型 S3 用戶端](crt-based-s3-client.md)或標準 Java 型 S3 非同步用戶端上建置時，S3 Transfer Manager 可以利用效能改善，例如[分段上傳 API](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) [和位元組範圍擷取](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance-guidelines.html#optimizing-performance-guidelines-get-range)。 [ S3 ](s3-async-client-multipart.md) 

使用 S3 Transfer Manager，您也可以即時監控傳輸進度，並暫停傳輸以供稍後執行。

## 開始使用
<a name="transfer-manager-prerequisites"></a>

### 將相依性新增至您的建置檔案
<a name="transfer-manager-add-dependency"></a>

若要使用具有增強分段效能的 S3 Transfer Manager，請設定具有必要相依性的建置檔案。

------
#### [ Use the AWS CRT-based S3 client ]

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.27.211</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-transfer-manager</artifactId>
    </dependency>
    <dependency>
        <groupId>software.amazon.awssdk.crt</groupId>
        <artifactId>aws-crt</artifactId>
        <version>0.29.1432</version>
    </dependency>
</dependencies>
```

1 [最新版本](https://central.sonatype.com/artifact/software.amazon.awssdk/bom)。 2[最新版本](https://central.sonatype.com/artifact/software.amazon.awssdk.crt/aws-crt)。

------
#### [ Use the Java-based S3 async client ]

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.27.211</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-transfer-manager</artifactId>
    </dependency>
</dependencies>
```

1 [最新版本](https://central.sonatype.com/artifact/software.amazon.awssdk/bom)。

------

### 建立 S3 Transfer Manager 的執行個體
<a name="transfer-manager-create"></a>

若要啟用平行傳輸，您必須傳入 AWS CRT 型 S3 用戶端或啟用分段的 Java 型 S3 非同步用戶端。下列範例示範如何使用自訂設定來設定 S3 Transfer Manager。

------
#### [ Use the AWS CRT-based S3 client ]

```
        S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder()
                .credentialsProvider(DefaultCredentialsProvider.create())
                .region(Region.US_EAST_1)
                .targetThroughputInGbps(20.0)
                .minimumPartSizeInBytes(8 * MB)
                .build();

        S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

------
#### [ Use the Java-based S3 async client ]

如果建置檔案中未包含`aws-crt`相依性，S3 Transfer Manager 會建置在適用於 Java 2.x 的 SDK 中使用的標準 Java 型 S3 非同步用戶端之上。

**S3 用戶端的自訂組態 - 需要啟用分段**

```
        S3AsyncClient s3AsyncClient = S3AsyncClient.builder()
                .multipartEnabled(true)
                .credentialsProvider(DefaultCredentialsProvider.create())
                .region(Region.US_EAST_1)
                .build();

        S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

**沒有 S3 用戶端的組態 - 自動啟用分段支援**

```
S3TransferManager transferManager = S3TransferManager.create();
```

------

## 將檔案上傳至 S3 儲存貯體
<a name="transfer-manager-upload"></a>

下列範例顯示檔案上傳範例，以及選用的 [LoggingTransferListener](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/progress/LoggingTransferListener.html)，這會記錄上傳進度。

若要使用 Amazon S3 S3，請將[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadFileRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadFileRequest.html)物件傳遞至 `S3TransferManager`的 [uploadFile](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#uploadFile(software.amazon.awssdk.transfer.s3.model.UploadFileRequest)) 方法。

從 `uploadFile`方法傳回的 [FileUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/FileUpload.html) 物件代表上傳程序。請求完成後，[CompletedFileUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedFileUpload.html) 物件會包含上傳的相關資訊。

```
    public void trackUploadFile(S3TransferManager transferManager, String bucketName,
                             String key, URI filePathURI) {
        UploadFileRequest uploadFileRequest = UploadFileRequest.builder()
                .putObjectRequest(b -> b.bucket(bucketName).key(key))
                .addTransferListener(LoggingTransferListener.create())  // Add listener.
                .source(Paths.get(filePathURI))
                .build();

        FileUpload fileUpload = transferManager.uploadFile(uploadFileRequest);

        fileUpload.completionFuture().join();
        /*
            The SDK provides a LoggingTransferListener implementation of the TransferListener interface.
            You can also implement the interface to provide your own logic.

            Configure log4J2 with settings such as the following.
                <Configuration status="WARN">
                    <Appenders>
                        <Console name="AlignedConsoleAppender" target="SYSTEM_OUT">
                            <PatternLayout pattern="%m%n"/>
                        </Console>
                    </Appenders>

                    <Loggers>
                        <logger name="software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener" level="INFO" additivity="false">
                            <AppenderRef ref="AlignedConsoleAppender"/>
                        </logger>
                    </Loggers>
                </Configuration>

            Log4J2 logs the progress. The following is example output for a 21.3 MB file upload.
                Transfer initiated...
                |                    | 0.0%
                |====                | 21.1%
                |============        | 60.5%
                |====================| 100.0%
                Transfer complete!
        */
    }
```

### 匯入
<a name="transfer-manager-upload-imports"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedFileUpload;
import software.amazon.awssdk.transfer.s3.model.FileUpload;
import software.amazon.awssdk.transfer.s3.model.UploadFileRequest;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.UUID;
```

## 從 S3 儲存貯體下載檔案
<a name="transfer-manager-download"></a>

下列範例顯示下載範例，以及選用的 [LoggingTransferListener](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/progress/LoggingTransferListener.html)，這會記錄下載進度。

若要使用 S3 Transfer Manager 從 S3 儲存貯體下載物件，請建置 [DownloadFileRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DownloadFileRequest.html) 物件並將其傳遞至 [downloadFile](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#downloadFile(software.amazon.awssdk.transfer.s3.model.DownloadFileRequest)) 方法。

的 `downloadFile`方法傳回`S3TransferManager`的 [FileDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/FileDownload.html) 物件代表檔案傳輸。下載完成後，[CompletedFileDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedFileDownload.html) 會包含下載相關資訊的存取權。

```
    public void trackDownloadFile(S3TransferManager transferManager, String bucketName,
                             String key, String downloadedFileWithPath) {
        DownloadFileRequest downloadFileRequest = DownloadFileRequest.builder()
                .getObjectRequest(b -> b.bucket(bucketName).key(key))
                .addTransferListener(LoggingTransferListener.create())  // Add listener.
                .destination(Paths.get(downloadedFileWithPath))
                .build();

        FileDownload downloadFile = transferManager.downloadFile(downloadFileRequest);

        CompletedFileDownload downloadResult = downloadFile.completionFuture().join();
        /*
            The SDK provides a LoggingTransferListener implementation of the TransferListener interface.
            You can also implement the interface to provide your own logic.

            Configure log4J2 with settings such as the following.
                <Configuration status="WARN">
                    <Appenders>
                        <Console name="AlignedConsoleAppender" target="SYSTEM_OUT">
                            <PatternLayout pattern="%m%n"/>
                        </Console>
                    </Appenders>

                    <Loggers>
                        <logger name="software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener" level="INFO" additivity="false">
                            <AppenderRef ref="AlignedConsoleAppender"/>
                        </logger>
                    </Loggers>
                </Configuration>

            Log4J2 logs the progress. The following is example output for a 21.3 MB file download.
                Transfer initiated...
                |=======             | 39.4%
                |===============     | 78.8%
                |====================| 100.0%
                Transfer complete!
        */
    }
```

### 匯入
<a name="transfer-manager-download-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.exception.SdkException;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedFileDownload;
import software.amazon.awssdk.transfer.s3.model.DownloadFileRequest;
import software.amazon.awssdk.transfer.s3.model.FileDownload;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
```

## 將 Amazon S3 物件複製到另一個儲存貯體
<a name="transfer-manager-copy"></a>

下列範例示範如何使用 S3 Transfer Manager 複製物件。

若要開始將物件從 S3 儲存貯體複製到另一個儲存貯體，請建立基本的 [CopyObjectRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/CopyObjectRequest.html) 執行個體。

接著，在 [CopyRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CopyRequest.html) `CopyObjectRequest`中包裝基本 ，供 S3 Transfer Manager 使用。

`S3TransferManager`的 `copy`方法傳回的`Copy`物件代表複製程序。複製程序完成後，[CompletedCopy](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedCopy.html) 物件會包含回應的詳細資訊。

```
    public String copyObject(S3TransferManager transferManager, String bucketName,
            String key, String destinationBucket, String destinationKey) {
        CopyObjectRequest copyObjectRequest = CopyObjectRequest.builder()
                .sourceBucket(bucketName)
                .sourceKey(key)
                .destinationBucket(destinationBucket)
                .destinationKey(destinationKey)
                .build();

        CopyRequest copyRequest = CopyRequest.builder()
                .copyObjectRequest(copyObjectRequest)
                .build();

        Copy copy = transferManager.copy(copyRequest);

        CompletedCopy completedCopy = copy.completionFuture().join();
        return completedCopy.response().copyObjectResult().eTag();
    }
```

**注意**  
若要使用 S3 Transfer Manager 執行跨區域複製，請在 AWS CRT 型 S3 用戶端建置器`crossRegionAccessEnabled`上啟用 ，如下列程式碼片段所示。  

```
S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder()
                .crossRegionAccessEnabled(true)
                .build();

S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

### 匯入
<a name="transfer-manager-copy-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.CopyObjectRequest;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedCopy;
import software.amazon.awssdk.transfer.s3.model.Copy;
import software.amazon.awssdk.transfer.s3.model.CopyRequest;

import java.util.UUID;
```

## 將本機目錄上傳至 S3 儲存貯體
<a name="transfer-manager-upload_directory"></a>

下列範例示範如何將本機目錄上傳至 S3。

首先呼叫`S3TransferManager`執行個體的 [uploadDirectory](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#uploadDirectory(software.amazon.awssdk.transfer.s3.model.UploadDirectoryRequest)) 方法，傳入 [UploadDirectoryRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadDirectoryRequest.html)。

[DirectoryUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DirectoryUpload.html) 物件代表上傳程序，該程序會在請求完成時產生 [CompletedDirectoryUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedDirectoryUpload.html)。`CompleteDirectoryUpload` 物件包含傳輸結果的相關資訊，包括哪些檔案無法傳輸。

```
    public Integer uploadDirectory(S3TransferManager transferManager,
            URI sourceDirectory, String bucketName) {
        DirectoryUpload directoryUpload = transferManager.uploadDirectory(UploadDirectoryRequest.builder()
                .source(Paths.get(sourceDirectory))
                .bucket(bucketName)
                .build());

        CompletedDirectoryUpload completedDirectoryUpload = directoryUpload.completionFuture().join();
        completedDirectoryUpload.failedTransfers()
                .forEach(fail -> logger.warn("Object [{}] failed to transfer", fail.toString()));
        return completedDirectoryUpload.failedTransfers().size();
    }
```

### 匯入
<a name="transfer-manager-upload_directory-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedDirectoryUpload;
import software.amazon.awssdk.transfer.s3.model.DirectoryUpload;
import software.amazon.awssdk.transfer.s3.model.UploadDirectoryRequest;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.UUID;
```

## 將 S3 儲存貯體物件下載至本機目錄
<a name="transfer-manager-download_directory"></a>

您可以將 S3 儲存貯體中的物件下載至本機目錄，如下列範例所示。

若要將 S3 儲存貯體中的物件下載至本機目錄，請先呼叫 Transfer Manager 的 [downloadDirectory](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#downloadDirectory(software.amazon.awssdk.transfer.s3.model.DownloadDirectoryRequest)) 方法，並傳入 [DownloadDirectoryRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DownloadDirectoryRequest.html)。

[DirectoryDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DirectoryDownload.html) 物件代表下載程序，該程序會在請求完成時產生 [CompletedDirectoryDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedDirectoryDownload.html)。`CompleteDirectoryDownload` 物件包含傳輸結果的相關資訊，包括哪些檔案無法傳輸。

```
    public Integer downloadObjectsToDirectory(S3TransferManager transferManager,
            URI destinationPathURI, String bucketName) {
        DirectoryDownload directoryDownload = transferManager.downloadDirectory(DownloadDirectoryRequest.builder()
                .destination(Paths.get(destinationPathURI))
                .bucket(bucketName)
                .build());
        CompletedDirectoryDownload completedDirectoryDownload = directoryDownload.completionFuture().join();

        completedDirectoryDownload.failedTransfers()
                .forEach(fail -> logger.warn("Object [{}] failed to transfer", fail.toString()));
        return completedDirectoryDownload.failedTransfers().size();
    }
```

### 匯入
<a name="transfer-manager-download_directory-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedDirectoryDownload;
import software.amazon.awssdk.transfer.s3.model.DirectoryDownload;
import software.amazon.awssdk.transfer.s3.model.DownloadDirectoryRequest;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
```

## 請參閱完整範例
<a name="transfer-manager-example-location"></a>

[GitHub 包含此頁面上所有範例的完整](https://github.com/awsdocs/aws-doc-sdk-examples/tree/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager)程式碼。

# 使用 S3 事件通知
<a name="examples-s3-event-notifications"></a>

為了協助您監控儲存貯體中的活動，Amazon S3 可以在發生特定事件時傳送通知。Amazon S3 使用者指南提供[儲存貯體可傳送之通知](https://docs.aws.amazon.com/AmazonS3/latest/userguide/EventNotifications.html#notification-how-to-overview)的相關資訊。

您可以使用適用於 Java 的 SDK 設定儲存貯體，將事件傳送至四個可能的目的地：
+ Amazon Simple Notification Service 主題
+ Amazon Simple Queue Service 佇列
+ AWS Lambda 函數
+ Amazon EventBridge

當您設定儲存貯體將事件傳送至 EventBridge 時，您可以設定 EventBridge 規則，將相同的事件散發到多個目的地。當您將儲存貯體設定為直接傳送至前三個目的地之一時，每個事件只能指定一個目的地類型。

在下一節中，您將了解如何使用適用於 Java 的 SDK 設定儲存貯體，以兩種方式傳送 S3 事件通知：直接傳送至 Amazon SQS 佇列和 EventBridge。

最後一節說明如何使用 S3 事件通知 API，以物件導向的方式使用通知。

## 設定儲存貯體以直接傳送至目的地
<a name="s3-event-conf-bucket-direct"></a>

下列範例會設定儲存貯體，以在*物件建立*事件或*物件標記*事件發生時傳送通知。

```
static void processS3Events(String bucketName, String queueArn) {
    // Configure the bucket to send Object Created and Object Tagging notifications to an existing SQS queue.
    s3Client.putBucketNotificationConfiguration(b -> b
            .notificationConfiguration(ncb -> ncb
                    .queueConfigurations(qcb -> qcb
                            .events(Event.S3_OBJECT_CREATED, Event.S3_OBJECT_TAGGING)
                            .queueArn(queueArn)))
                    .bucket(bucketName)
    );
}
```

上面顯示的程式碼會設定一個佇列來接收兩種類型的事件。方便使用， `queueConfigurations`方法可讓您視需要設定多個佇列目的地。此外，您可以在 `notificationConfiguration`方法中設定其他目的地，例如一或多個 Amazon SNS 主題或一或多個 Lambda 函數。下列程式碼片段顯示具有兩個佇列和三種目的地類型的範例。

```
s3Client.putBucketNotificationConfiguration(b -> b
                .notificationConfiguration(ncb -> ncb
                        .queueConfigurations(qcb -> qcb
                                .events(Event.S3_OBJECT_CREATED, Event.S3_OBJECT_TAGGING)
                                .queueArn(queueArn), 
                                qcb2 -> qcb2.<...>)
                        .topicConfigurations(tcb -> tcb.<...>)
                        .lambdaFunctionConfigurations(lfcb -> lfcb.<...>))
                        .bucket(bucketName)
        );
```

程式碼範例 GitHub 儲存庫包含將 S3 事件通知直接傳送到佇列[的完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/ProcessS3EventNotification.java)。

## 設定要傳送至 EventBridge 的儲存貯體
<a name="s3-event-conf-bucket-eventbridge"></a>

下列範例會將儲存貯體設定為傳送通知至 EventBridge。

```
public static String setBucketNotificationToEventBridge(String bucketName) {
    // Enable bucket to emit S3 Event notifications to EventBridge.
    s3Client.putBucketNotificationConfiguration(b -> b
            .bucket(bucketName)
            .notificationConfiguration(b1 -> b1
                    .eventBridgeConfiguration(SdkBuilder::build))
    .build());
```

當您設定儲存貯體將事件傳送至 EventBridge 時，您只需指出 EventBridge 目的地，而不是 EventBridge 將發送的事件類型或最終目的地。您可以使用 Java 開發套件的 EventBridge 用戶端來設定最終目標和事件類型。

下列程式碼說明如何設定 EventBridge 將*物件建立*的事件散發至主題和佇列。

```
   public static String configureEventBridge(String topicArn, String queueArn) {
        try {
            // Create an EventBridge rule to route Object Created notifications.
            PutRuleRequest putRuleRequest = PutRuleRequest.builder()
                    .name(RULE_NAME)
                    .eventPattern("""
                            {
                              "source": ["aws.s3"],
                              "detail-type": ["Object Created"],
                              "detail": {
                                "bucket": {
                                  "name": ["%s"]
                                }
                              }
                            }
                            """.formatted(bucketName))
                    .build();

            // Add the rule to the default event bus.
            PutRuleResponse putRuleResponse = eventBridgeClient.putRule(putRuleRequest)
                    .whenComplete((r, t) -> {
                        if (t != null) {
                            logger.error("Error creating event bus rule: " + t.getMessage(), t);
                            throw new RuntimeException(t.getCause().getMessage(), t);
                        }
                        logger.info("Event bus rule creation request sent successfully. ARN is: {}", r.ruleArn());
                    }).join();

            // Add the existing SNS topic and SQS queue as targets to the rule.
            eventBridgeClient.putTargets(b -> b
                    .eventBusName("default")
                    .rule(RULE_NAME)
                    .targets(List.of (
                            Target.builder()
                                    .arn(queueArn)
                                    .id("Queue")
                                    .build(),
                            Target.builder()
                                    .arn(topicArn)
                                    .id("Topic")
                                    .build())
                            )
                    ).join();
            return putRuleResponse.ruleArn();
        } catch (S3Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return null;
    }
```

若要在 Java 程式碼中使用 EventBridge，請將`eventbridge`成品的相依性新增至 Maven `pom.xml` 檔案。

```
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>eventbridge</artifactId>
</dependency>
```

程式碼範例 GitHub 儲存庫包含傳送 S3 事件通知至 EventBridge，然後傳送至主題和佇列[的完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/PutBucketS3EventNotificationEventBridge.java)。

## 使用 S3 事件通知 API 來處理事件
<a name="s3-event-notification-read"></a>

目的地收到 S3 通知事件後，您可以使用 S3 事件通知 API，以物件導向的方式處理它們。您可以使用 S3 事件通知 API 來處理直接分派至目標的事件通知 （如[第一個範例](#s3-event-conf-bucket-direct)所示），但不適用於透過 EventBridge 路由的通知。儲存貯體傳送至 EventBridge 的 S3 事件通知包含 S3 事件通知 API 目前未處理[的不同結構](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html#ev-events-list)。

### 新增相依性
<a name="s3-event-notifications-dep"></a>

S3 事件通知 API 已搭配適用於 Java 的 SDK 2.x 2.25.11 版發行。

若要使用 S3 事件通知 API，請將必要的相依性元素新增至 Maven`pom.xml`，如下列程式碼片段所示。

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.X.X1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-event-notifications</artifactId>
    </dependency>
</dependencies>
```

1 [最新版本](https://central.sonatype.com/artifact/software.amazon.awssdk/bom)。

### 使用 `S3EventNotification`類別
<a name="s3-event-notifications-use"></a>

#### 從 JSON 字串建立`S3EventNotification`執行個體
<a name="s3-event-notifications-use-from-json"></a>

若要將 JSON 字串轉換為`S3EventNotification`物件，請使用 `S3EventNotification`類別的靜態方法，如下列範例所示。

```
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotificationRecord
import software.amazon.awssdk.services.sqs.model.Message; 

public class S3EventNotificationExample {
    ...
    
    void receiveMessage(Message message) {
       // Message received from SQSClient.
       String sqsEventBody = message.body();
       S3EventNotification s3EventNotification = S3EventNotification.fromJson(sqsEventBody);
    
       // Use getRecords() to access all the records in the notification.                                                                                                       
       List<S3EventNotificationRecord> records = s3EventNotification.getRecords();   
    
        S3EventNotificationRecord record = records.stream().findFirst();
        // Use getters on the record to access individual attributes.
        String awsRegion = record.getAwsRegion();
        String eventName = record.getEventName();
        String eventSource = record.getEventSource();                                                                                                   
    }
}
```

在此範例中， `fromJson`方法會將 JSON 字串轉換為 `S3EventNotification` 物件。JSON 字串中缺少的欄位會導致對應 Java 物件欄位中`null`的值，且 JSON 中的任何額外欄位都會遭到忽略。

您可以在 APIs 參考中找到事件通知記錄的其他 API`[S3EventNotificationRecord](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/eventnotifications/s3/model/S3EventNotificationRecord.html)`。

#### 將`S3EventNotification`執行個體轉換為 JSON 字串
<a name="s3-event-notifications-use-to-json"></a>

使用 `toJson`（或 `toJsonPretty`) 方法將`S3EventNotification`物件轉換為 JSON 字串，如下列範例所示。

```
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification

public class S3EventNotificationExample {
    ...

    void toJsonString(S3EventNotification event) {

        String json = event.toJson();
        String jsonPretty = event.toJsonPretty();

        System.out.println("JSON: " + json);
        System.out.println("Pretty JSON: " + jsonPretty);
    }
}
```

如果 `GlacierEventData`、`IntelligentTieringEventData`、 `ReplicationEventData`和 的欄位是 ，`LifecycleEventData`則會從 JSON 中排除`null`。其他`null`欄位將序列化為 `null`。

以下顯示 S3 物件標記事件的 `toJsonPretty`方法輸出範例。

```
{
  "Records" : [ {
    "eventVersion" : "2.3",
    "eventSource" : "aws:s3",
    "awsRegion" : "us-east-1",
    "eventTime" : "2024-07-19T20:09:18.551Z",
    "eventName" : "ObjectTagging:Put",
    "userIdentity" : {
      "principalId" : "AWS:XXXXXXXXXXX"
    },
    "requestParameters" : {
      "sourceIPAddress" : "XXX.XX.XX.XX"
    },
    "responseElements" : {
      "x-amz-request-id" : "XXXXXXXXXXXX",
      "x-amz-id-2" : "XXXXXXXXXXXXX"
    },
    "s3" : {
      "s3SchemaVersion" : "1.0",
      "configurationId" : "XXXXXXXXXXXXX",
      "bucket" : {
        "name" : "amzn-s3-demo-bucket",
        "ownerIdentity" : {
          "principalId" : "XXXXXXXXXXX"
        },
        "arn" : "arn:aws:s3:::XXXXXXXXXX"
      },
      "object" : {
        "key" : "akey",
        "size" : null,
        "eTag" : "XXXXXXXXXX",
        "versionId" : null,
        "sequencer" : null
      }
    }
  } ]
}
```

GitHub 提供[完整的範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/75c3daadf750406156fc87fa30ee499a206b4a36/javav2/example_code/s3/src/main/java/com/example/s3/ProcessS3EventNotification.java#L117)，說明如何使用 API 來處理 Amazon SQS 佇列收到的通知。

## 使用 Java 程式庫處理 Lambda 中的 S3 事件： AWS SDK for Java 2.x 和 `aws-lambda-java-events`
<a name="s3-event-notif-processing-options"></a>

與其使用適用於 Java 的 SDK 2.x 在 Lambda 函數中處理 Amazon S3 事件通知，您可以在 3.x.x 版使用`[aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs/tree/main/aws-lambda-java-events)`程式庫。 會獨立 AWS 維護`aws-lambda-java-events`程式庫，並且具有自己的相依性要求。`aws-lambda-java-events` 程式庫僅適用於 Lambda 函數中的 S3 事件，而適用於 Java 的 SDK 2.x 僅適用於 Lambda 函數、Amazon SNS 和 Amazon SQS 中的 S3 事件。

這兩種方法都使用類似的 APIs，以物件導向的方式建立 JSON 事件通知承載的模型。下表顯示使用兩種方法之間的顯著差異。


****  

|  | 適用於 Java 的 AWS SDK | aws-lambda-java-events 程式庫 | 
| --- | --- | --- | 
| 套件命名 |  `software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification`  | com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification | 
| RequestHandler 參數 |  撰寫 Lambda 函數的`RequestHandler`實作以接收 JSON 字串： <pre>import com.amazonaws.services.lambda.runtime.Context;<br />import com.amazonaws.services.lambda.runtime.RequestHandler;<br />import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification;<br /><br />public class Handler implements RequestHandler<String, String> {<br /><br />    @Override<br />        public String handleRequest(String jsonS3Event, Context context) {<br />            S3EventNotification s3Event = S3EventNotification<br />                                             .fromJson(jsonS3Event);<br />            // Work with the s3Event object.        <br />            ...<br />    }<br />}</pre>  | 撰寫 Lambda 函數的RequestHandler實作以接收S3Event物件：<pre>import com.amazonaws.services.lambda.runtime.Context;<br />import com.amazonaws.services.lambda.runtime.RequestHandler;<br />import com.amazonaws.services.lambda.runtime.events.S3Event;<br /><br />public class Handler implements RequestHandler<S3Event, String> {<br /><br />    @Override<br />        public String handleRequest(S3Event s3event, Context context) {<br />            // Work with the s3Event object.        <br />            ...<br />    }<br />}</pre> | 
| Maven 相依性 |  <pre><dependencyManagement><br />    <dependencies><br />        <dependency><br />            <groupId>software.amazon.awssdk</groupId><br />            <artifactId>bom</artifactId><br />            <version>2.X.X</version><br />            <type>pom</type><br />            <scope>import</scope><br />        </dependency><br />    </dependencies><br /></dependencyManagement><br /><dependencies><br />    <dependency><br />        <groupId>software.amazon.awssdk</groupId><br />        <artifactId>s3-event-notifications</artifactId><br />    </dependency><br />    <!-- Add other SDK dependencies that you need. --><br /></dependencies></pre>  |  <pre><dependencyManagement><br />    <dependencies><br />        <dependency><br />            <groupId>software.amazon.awssdk</groupId><br />            <artifactId>bom</artifactId><br />            <version>2.X.X</version><br />            <type>pom</type><br />            <scope>import</scope><br />        </dependency><br />    </dependencies><br /></dependencyManagement><br /><dependencies><br />    <!-- The following two dependencies are for the <br />         aws-lambda-java-events library. --><br />    <dependency><br />        <groupId>com.amazonaws</groupId><br />        <artifactId>aws-lambda-java-core</artifactId><br />        <version>1.2.3</version>     <br />    </dependency><br />    <dependency><br />        <groupId>com.amazonaws</groupId><br />        <artifactId>aws-lambda-java-events</artifactId><br />        <version>3.15.0</version><br />    </dependency><br />    <!-- Add other SDK dependencies that you need. --><br /></dependencies></pre>  | 