

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

# 在 Amazon S3 中檢查資料上傳的物件完整性
<a name="checking-object-integrity-upload"></a>

Amazon S3 使用檢查總和值驗證上傳或下載作業期間的資料完整性。當您上傳資料時， AWS SDK 和 AWS 管理主控台 會使用您選擇的檢查總和演算法，在資料傳輸之前運算檢查總和值。然後，S3 會獨立計算資料的檢查總和，並根據提供的檢查總和值進行驗證。只有在確認傳輸過程中保持資料完整性後，才會接受物件。S3 會將檢查總和值同時儲存為物件中繼資料和物件本身。

要驗證物件的資料完整性，您可以在下載過程中請求檢查總和值。此驗證方法在加密模式、物件大小、儲存類別以及單一部分上傳和分段上傳過程中，均能保持一致性。若要變更上傳的檢查總和演算法，您可以複製個別物件，或使用多個物件的批次複製。

對於單一部分上傳，您可以提供檢查總和值做為標頭。您可以提供預先計算的值，或讓 AWS SDK 在上傳期間計算值。如果 S3 計算的檢查總和值符合您提供的值，則會接受請求。如果值不相符，則會拒絕請求。

對於分段上傳， AWS SDKs 可以自動為區塊上傳建立結尾檢查總和。當您使用尾部檢查總和時，Amazon S3 會使用您指定的演算法為每個部分產生檢查總和值，並將檢查總和值附加到分塊上傳請求的末尾。S3 會在單一傳遞中執行驗證與上傳作業，以提高效率。如需詳細資訊，請參閱[使用追蹤檢查總和](#trailing-checksums)。

## 使用支持的檢查總和演算法
<a name="using-additional-checksums"></a>

您可以透過 Amazon S3 選擇檢查總和演算法，用於在上傳過程中驗證資料。然後，指定的檢查總和演算法會與您的物件儲存在一起，以便在下載期間用於驗證資料完整性。您可以選擇下列安全雜湊演算法 (SHA) 或循環冗餘檢查 (CRC) 檢查總和演算法來計算檢查總和值：
+ CRC-64/NVME (`CRC64NVME`)
+ CRC-32 (`CRC32`)
+ CRC-32C (`CRC32C`)
+ SHA-1 (`SHA1`)
+ SHA-256 (`SHA256`)
+ MD5 (`MD5`)
**注意**  
`content-MD5` 標頭僅可透過 S3 ETag 用於使用 SSE-S3 加密的單一部分上傳作業 (`PUT` 操作) 所上傳的物件。

此外，您還可以使用 Content-MD5 標頭來提供每個請求的檢查總和。

當您[上傳物件](https://docs.aws.amazon.com/AmazonS3/latest/userguide/upload-objects.html)時，您可以指定要使用的演算法：
+ **使用 時 AWS 管理主控台**，請選擇您要使用的檢查總和演算法。您可以選擇指定物件的檢查總和值。Amazon S3 接收物件時，它會使用您指定的演算法計算檢查總和。如果兩個檢查總和值不相符，Amazon S3 會產生錯誤。
+ **當您使用 SDK 時**，請注意下列事項：
  + 將 `ChecksumAlgorithm` 參數設定為您希望 Amazon S3 使用的演算法。如果您已有預先計算的檢查總和，您可以將檢查總和值傳遞至 AWS SDK，而 SDK 會在請求中包含該值。如果您未傳遞檢查總和值或未指定檢查總和演算法，SDK 會自動為您計算檢查總和值並在請求中包含該值，以提供完整性保護。如果個別檢查總和值不符合檢查總和演算法的設定值，Amazon S3 的請求會失敗並顯示 `BadDigest` 錯誤。
  + 如果您使用的是升級的 AWS SDK，則 SDK 會為您選擇檢查總和演算法。不過，您可以覆寫此檢查總和演算法。
  + 如果您未指定檢查總和演算法，且 SDK 也不會為您計算檢查總和，則 S3 會自動選擇 CRC-64/NVME (`CRC64NVME`) 檢查總和演算法。
+ **當您使用 REST API 時**，請勿使用 `x-amz-sdk-checksum-algorithm` 參數。反之，請使用演算法特定的標頭之一 (例如 `x-amz-checksum-crc32`)。

若要將這些檢查總和值套用至已上傳至 Amazon S3 的物件，您可以複製該物件，並指定您想要使用現有的或新的檢查總和演算法。如果您未指定演算法，S3 會使用現有的演算法。如果來源物件沒有指定的檢查總和演算法或檢查總和值，Amazon S3 會使用 CRC-64/NVME 演算法來計算目的地物件的檢查總和值。您也可以在使用 [S3 Batch Operations](https://docs.aws.amazon.com/AmazonS3/latest/userguide/batch-ops.html) 複製物件時指定檢查總和演算法。

**重要**  
如果您搭配**檢查總和**使用分段上傳以進行合成 (或成分等級) 檢查總和，分段上傳的成分編號必須是連續的，並且從 1 開始。如果您嘗試使用非連續的組件編號完成分段上傳請求，則 Amazon S3 會產生 `HTTP 500 Internal Server` 錯誤。

## 完整物件與複合檢查總和類型
<a name="ChecksumTypes-Uploads"></a>

在 Amazon S3 中，支援兩種檢查總和類型：
+ **完整物件檢查總和：**完整物件檢查總和是根據分段上傳的所有內容來計算，涵蓋從第一個組件第一個位元組到最後一個組件最後一個位元組的所有資料。請注意，如果您使用 AWS 管理主控台 上傳小於 16 MB 的物件，則僅支援完整的物件檢查總和類型。
**注意**  
所有 PUT 請求都需要完整物件檢查總和類型。如果您要透過 PUT 請求上傳物件，則必須指定完整的物件檢查總和類型。
+ **複合檢查總和：**複合檢查總和是根據分段上傳中每個組件的個別檢查總和來計算。此方法不會根據所有資料內容計算檢查總和，而是彙總組件層級檢查總和 (從第一個組件到最後一個組件)，以產生完整物件的單一合併檢查總和。如果您使用分段上傳來上傳物件，則必須指定合成檢查總和類型。
**注意**  
當物件以分段上傳方式上傳時，該物件的實體標籤 (ETag) 不會是整個物件的 MD5 摘要。反之，Amazon S3 會在上傳時計算每個組件的 MD5 摘要。MD5 摘要用於確定最終物件的 ETag。Amazon S3 將 MD5 摘要的位元連接在一起，然後計算這些 MD5 摘要的連接值。在建立 ETag 的最後一個步驟中，Amazon S3 會在結尾加上破折號，後面接著組件總數。

Amazon S3 支援下列完整物件和複合檢查總和演算法類型：
+ CRC-64/NVME (`CRC64NVME`)：僅支援完整的物件檢查總和類型。
+ CRC-32 (`CRC32`)：支援完整物件與合成檢查總和類型。
+ CRC-32C (`CRC32C`)：支援完整物件與合成檢查總和類型。
+ SHA-1 (`SHA1`)：支援完整物件與合成檢查總和類型。
+ SHA-256 (`SHA256`)：支援完整物件與合成檢查總和類型。
+ MD5 (`MD5`)：支援完整物件與合成演算法類型。

### 單一組件上傳
<a name="SinglePartUploads-Checksums"></a>

以單一組件上傳 (使用 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html#API_PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html#API_PutObject)) 之物件的檢查總和會視為完整物件檢查總和。當您在 Amazon S3 主控台中上傳物件時，您可以選擇希望 S3 使用的檢查總和演算法，也可以選擇提供預先計算的值。Amazon S3 會先驗證預先計算的檢查總和值，然後再儲存物件及其檢查總和值。您可以在下載物件期間請求檢查總和值時，驗證物件的資料完整性。

### 分段上傳
<a name="MultipartUploads-Checksums"></a>

當您使用 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_MultipartUpload.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_MultipartUpload.html) API 以多個組件上傳物件時，您可以指定希望 Amazon S3 使用的檢查總和演算法，以及檢查總和類型 (完整物件或複合)。

 下表指出分段上傳中每個檢查總和演算法支援的檢查總和演算法類型：


| 檢查總和演算法 | 完整物件 | 複合 | 
| --- | --- | --- | 
| CRC-64/NVME (CRC64NVME) | 是 | 否 | 
| CRC-32 (CRC32) | 是 | 是 | 
| CRC-32C (CRC32C) | 是 | 是 | 
| SHA-1 (SHA1) | 否 | 是 | 
| SHA-256 (SHA256) | 否 | 是 | 

## 對分段上傳使用完整物件檢查總和
<a name="Full-object-checksums"></a>

建立或執行分段上傳時，您可以使用完整物件檢查總和來驗證上傳。這表示您可以為 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_MultipartUpload.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_MultipartUpload.html) API 提供檢查總和演算法，從而簡化完整性驗證工具，因為您不再需要追蹤上傳物件的組件界限。您可以提供 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html) 請求中整個物件的檢查總和，以及物件大小。

當您在分段上傳期間提供完整的物件檢查總和時， AWS 開發套件會將檢查總和傳遞給 Amazon S3，S3 會驗證物件完整性伺服器端，並將其與接收的值進行比較。如果值相符，則 Amazon S3 會儲存物件。如果這兩個值不相符，S3 的請求會失敗並顯示 `BadDigest` 錯誤。物件的檢查總和也會儲存在物件中繼資料內，以供稍後用來驗證物件的資料完整性。

對於完整物件檢查總和，您可以在 S3 中使用 CRC-64/NVME (`CRC64NVME`)、CRC-32 (`CRC32`) 或 CRC-32C (`CRC32C`) 檢查總和演算法。分段上傳中的完整物件檢查總和僅適用於 CRC 型檢查總和，因為其可線性化為完整物件檢查總和。此線性化可讓 Amazon S3 平行處理您的請求，以提升效能。特別是，S3 可以從組件層級檢查總和計算整個物件的檢查總和。這種驗證類型不適用於其他演算法 (例如 SHA 和 MD5)。由於 S3 具有預設完整性保護，如果物件在沒有檢查總和的情況下上傳，S3 會自動將建議的完整物件 CRC-64/NVME (`CRC64NVME`) 檢查總和演算法連接至物件。

**注意**  
若要啟動分段上傳，您可以指定檢查總和演算法和完整物件檢查總和類型。指定檢查總和演算法和完整物件檢查總和類型之後，您可以為分段上傳提供完整物件檢查總和值。

## 對分段上傳使用組件層級檢查總和
<a name="Part-level-checksums"></a>

當物件上傳至 Amazon S3 時，可作為單一物件上傳，或透過分段上傳程序作為多個組件上傳。您可以選擇分段上傳的**檢查總和**類型。對於分段上傳組件層級檢查總和 (或複合檢查總和)，Amazon S3 會使用指定的檢查總和演算法來計算每個組件的檢查總和。您可以使用 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) 來提供每個組件的檢查總和值。如果您嘗試在 Amazon S3 主控台中上傳的物件設定為使用 CRC-64/NVME (`CRC64NVME`) 檢查總和演算法且超過 16 MB，則會自動將其指定為完整物件檢查總和。

然後，Amazon S3 會使用儲存的組件層級檢查總和值來確認每個組件都已正確上傳。為整個物件提供每個組件的檢查總和時，S3 會使用每個組件的儲存檢查總和值在內部計算完整物件檢查總和，並將其與提供的檢查總和值進行比較。這可將運算成本降到最低，因為 S3 可以使用組件的檢查總和來計算整個物件的檢查總和。如需分段上傳的詳細資訊，請參閱[在 Amazon S3 中使用分段上傳來上傳和複製物件](mpuoverview.md)和[對分段上傳使用完整物件檢查總和](#Full-object-checksums)。

物件完全上傳之後，您可以使用最終計算的檢查總和來驗證物件的資料完整性。

分段上傳組件時，請注意下列事項：
+ 若要擷取物件的相關資訊 (包括構成整個物件的組件數目)，您可以使用 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAttributes.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObjectAttributes.html) 操作。透過額外的檢查總和，您也可以復原每個組件的資訊 (包括組件的檢查總和值)。
+ 對於已完成的上傳，您可以使用 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) 或 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html) 操作，並指定組件編號或符合單一組件的位元組範圍，來取得個別組件的檢查總和。如果您想要在分段上傳仍在進行時，擷取個別組件的檢查總和值，則可以使用 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html)。
+ 由於 Amazon S3 計算分段上傳物件的檢查總和的方式，物件的檢查總和值可能會因為複製而變更。如果您使用 SDK 或 REST API，並且呼叫 [https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html)，則 Amazon S3 可複製不超過 `CopyObject` API 操作大小限制的任何物件。無論物件是由單個請求上傳還是作為分段上傳的一部分，Amazon S3 都會將此複製作為單獨操作進行。使用複製命令，物件的檢查總和是完整物件的直接檢查總和。如果物件最初是使用分段上傳方式進行上傳，即使資料沒有變更，檢查總和值也會變更。
+ 超過 `CopyObject` API 操作大小上限的物件必須使用[分段複製命令](https://docs.aws.amazon.com/AmazonS3/latest/userguide/CopyingObjectsMPUapi.html)。
+ 當您使用 執行某些操作時 AWS 管理主控台，如果物件大小大於 16 MB，Amazon S3 會使用分段上傳。

## 檢查總和方法
<a name="ChecksumMethods"></a>

上傳物件之後，您可以取得檢查總和值，並將其與相同檢查總和演算法類型之預先計算或先前儲存的檢查總和值進行比較。下列範例顯示您可以使用哪些檢查總和計算方法來驗證資料完整性。

### 使用 S3 主控台
<a name="CheckObjectIntegrityConsole"></a>

若要進一步了解如何使用主控台，以及如何指定上傳物件時要使用的檢查總和演算法，請參閱 [上傳物件](upload-objects.md) 和[教學課程：使用其他檢查總和來檢查 Amazon S3 中資料的完整性](https://aws.amazon.com/getting-started/hands-on/amazon-s3-with-additional-checksums/?ref=docs_gateway/amazons3/checking-object-integrity.html)。

### 使用 AWS SDKs
<a name="CheckObjectIntegritySDK"></a>

下列範例示範如何使用 AWS SDKs 上傳包含分段上傳的大型檔案、下載大型檔案，以及驗證分段上傳檔案，全都使用 SHA-256 進行檔案驗證。

------
#### [ Java ]

**Example 範例：使用 SHA-256 上傳、下載和驗證大型檔案**  
如需建立和測試工作範例的說明，請參閱《 適用於 Java 的 AWS SDK 開發人員指南》中的[入門](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/getting-started.html)。  

```
    import software.amazon.awssdk.auth.credentials.AwsCredentials;
    import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
    import software.amazon.awssdk.core.ResponseInputStream;
    import software.amazon.awssdk.core.sync.RequestBody;
    import software.amazon.awssdk.regions.Region;
    import software.amazon.awssdk.services.s3.S3Client;
    import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest;
    import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm;
    import software.amazon.awssdk.services.s3.model.ChecksumMode;
    import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest;
    import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse;
    import software.amazon.awssdk.services.s3.model.CompletedMultipartUpload;
    import software.amazon.awssdk.services.s3.model.CompletedPart;
    import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest;
    import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse;
    import software.amazon.awssdk.services.s3.model.GetObjectAttributesRequest;
    import software.amazon.awssdk.services.s3.model.GetObjectAttributesResponse;
    import software.amazon.awssdk.services.s3.model.GetObjectRequest;
    import software.amazon.awssdk.services.s3.model.GetObjectResponse;
    import software.amazon.awssdk.services.s3.model.GetObjectTaggingRequest;
    import software.amazon.awssdk.services.s3.model.ObjectAttributes;
    import software.amazon.awssdk.services.s3.model.PutObjectTaggingRequest;
    import software.amazon.awssdk.services.s3.model.Tag;
    import software.amazon.awssdk.services.s3.model.Tagging;
    import software.amazon.awssdk.services.s3.model.UploadPartRequest;
    import software.amazon.awssdk.services.s3.model.UploadPartResponse;
     
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.nio.ByteBuffer;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.ArrayList;
    import java.util.Base64;
    import java.util.List;
     
    public class LargeObjectValidation {
        private static String FILE_NAME = "sample.file";
        private static String BUCKET = "sample-bucket";
        //Optional, if you want a method of storing the full multipart object checksum in S3.
        private static String CHECKSUM_TAG_KEYNAME = "fullObjectChecksum";
        //If you have existing full-object checksums that you need to validate against, you can do the full object validation on a sequential upload.
        private static String SHA256_FILE_BYTES = "htCM5g7ZNdoSw8bN/mkgiAhXt5MFoVowVg+LE9aIQmI=";
        //Example Chunk Size - this must be greater than or equal to 5MB.
        private static int CHUNK_SIZE = 5 * 1024 * 1024;
     
        public static void main(String[] args) {
            S3Client s3Client = S3Client.builder()
                    .region(Region.US_EAST_1)
                    .credentialsProvider(new AwsCredentialsProvider() {
                        @Override
                        public AwsCredentials resolveCredentials() {
                            return new AwsCredentials() {
                                @Override
                                public String accessKeyId() {
                                    return Constants.ACCESS_KEY;
                                }
     
                                @Override
                                public String secretAccessKey() {
                                    return Constants.SECRET;
                                }
                            };
                        }
                    })
                    .build();
            uploadLargeFileBracketedByChecksum(s3Client);
            downloadLargeFileBracketedByChecksum(s3Client);
            validateExistingFileAgainstS3Checksum(s3Client);
        }
     
        public static void uploadLargeFileBracketedByChecksum(S3Client s3Client) {
            System.out.println("Starting uploading file validation");
            File file = new File(FILE_NAME);
            try (InputStream in = new FileInputStream(file)) {
                MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
                CreateMultipartUploadRequest createMultipartUploadRequest = CreateMultipartUploadRequest.builder()
                        .bucket(BUCKET)
                        .key(FILE_NAME)
                        .checksumAlgorithm(ChecksumAlgorithm.SHA256)
                        .build();
                CreateMultipartUploadResponse createdUpload = s3Client.createMultipartUpload(createMultipartUploadRequest);
                List<CompletedPart> completedParts = new ArrayList<CompletedPart>();
                int partNumber = 1;
                byte[] buffer = new byte[CHUNK_SIZE];
                int read = in.read(buffer);
                while (read != -1) {
                    UploadPartRequest uploadPartRequest = UploadPartRequest.builder()
                            .partNumber(partNumber).uploadId(createdUpload.uploadId()).key(FILE_NAME).bucket(BUCKET).checksumAlgorithm(ChecksumAlgorithm.SHA256).build();
                    UploadPartResponse uploadedPart = s3Client.uploadPart(uploadPartRequest, RequestBody.fromByteBuffer(ByteBuffer.wrap(buffer, 0, read)));
                    CompletedPart part = CompletedPart.builder().partNumber(partNumber).checksumSHA256(uploadedPart.checksumSHA256()).eTag(uploadedPart.eTag()).build();
                    completedParts.add(part);
                    sha256.update(buffer, 0, read);
                    read = in.read(buffer);
                    partNumber++;
                }
                String fullObjectChecksum = Base64.getEncoder().encodeToString(sha256.digest());
                if (!fullObjectChecksum.equals(SHA256_FILE_BYTES)) {
                    //Because the SHA256 is uploaded after the part is uploaded; the upload is bracketed and the full object can be fully validated.
                    s3Client.abortMultipartUpload(AbortMultipartUploadRequest.builder().bucket(BUCKET).key(FILE_NAME).uploadId(createdUpload.uploadId()).build());
                    throw new IOException("Byte mismatch between stored checksum and upload, do not proceed with upload and cleanup");
                }
                CompletedMultipartUpload completedMultipartUpload = CompletedMultipartUpload.builder().parts(completedParts).build();
                CompleteMultipartUploadResponse completedUploadResponse = s3Client.completeMultipartUpload(
                        CompleteMultipartUploadRequest.builder().bucket(BUCKET).key(FILE_NAME).uploadId(createdUpload.uploadId()).multipartUpload(completedMultipartUpload).build());
                Tag checksumTag = Tag.builder().key(CHECKSUM_TAG_KEYNAME).value(fullObjectChecksum).build();
                //Optionally, if you need the full object checksum stored with the file; you could add it as a tag after completion.
                s3Client.putObjectTagging(PutObjectTaggingRequest.builder().bucket(BUCKET).key(FILE_NAME).tagging(Tagging.builder().tagSet(checksumTag).build()).build());
            } catch (IOException | NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            GetObjectAttributesResponse
                    objectAttributes = s3Client.getObjectAttributes(GetObjectAttributesRequest.builder().bucket(BUCKET).key(FILE_NAME)
                    .objectAttributes(ObjectAttributes.OBJECT_PARTS, ObjectAttributes.CHECKSUM).build());
            System.out.println(objectAttributes.objectParts().parts());
            System.out.println(objectAttributes.checksum().checksumSHA256());
        }
     
        public static void downloadLargeFileBracketedByChecksum(S3Client s3Client) {
            System.out.println("Starting downloading file validation");
            File file = new File("DOWNLOADED_" + FILE_NAME);
            try (OutputStream out = new FileOutputStream(file)) {
                GetObjectAttributesResponse
                        objectAttributes = s3Client.getObjectAttributes(GetObjectAttributesRequest.builder().bucket(BUCKET).key(FILE_NAME)
                        .objectAttributes(ObjectAttributes.OBJECT_PARTS, ObjectAttributes.CHECKSUM).build());
                //Optionally if you need the full object checksum, you can grab a tag you added on the upload
                List<Tag> objectTags = s3Client.getObjectTagging(GetObjectTaggingRequest.builder().bucket(BUCKET).key(FILE_NAME).build()).tagSet();
                String fullObjectChecksum = null;
                for (Tag objectTag : objectTags) {
                    if (objectTag.key().equals(CHECKSUM_TAG_KEYNAME)) {
                        fullObjectChecksum = objectTag.value();
                        break;
                    }
                }
                MessageDigest sha256FullObject = MessageDigest.getInstance("SHA-256");
                MessageDigest sha256ChecksumOfChecksums = MessageDigest.getInstance("SHA-256");
     
                //If you retrieve the object in parts, and set the ChecksumMode to enabled, the SDK will automatically validate the part checksum
                for (int partNumber = 1; partNumber <= objectAttributes.objectParts().totalPartsCount(); partNumber++) {
                    MessageDigest sha256Part = MessageDigest.getInstance("SHA-256");
                    ResponseInputStream<GetObjectResponse> response = s3Client.getObject(GetObjectRequest.builder().bucket(BUCKET).key(FILE_NAME).partNumber(partNumber).checksumMode(ChecksumMode.ENABLED).build());
                    GetObjectResponse getObjectResponse = response.response();
                    byte[] buffer = new byte[CHUNK_SIZE];
                    int read = response.read(buffer);
                    while (read != -1) {
                        out.write(buffer, 0, read);
                        sha256FullObject.update(buffer, 0, read);
                        sha256Part.update(buffer, 0, read);
                        read = response.read(buffer);
                    }
                    byte[] sha256PartBytes = sha256Part.digest();
                    sha256ChecksumOfChecksums.update(sha256PartBytes);
                    //Optionally, you can do an additional manual validation again the part checksum if needed in addition to the SDK check
                    String base64PartChecksum = Base64.getEncoder().encodeToString(sha256PartBytes);
                    String base64PartChecksumFromObjectAttributes = objectAttributes.objectParts().parts().get(partNumber - 1).checksumSHA256();
                    if (!base64PartChecksum.equals(getObjectResponse.checksumSHA256()) || !base64PartChecksum.equals(base64PartChecksumFromObjectAttributes)) {
                        throw new IOException("Part checksum didn't match for the part");
                    }
                    System.out.println(partNumber + " " + base64PartChecksum);
                }
                //Before finalizing, do the final checksum validation.
                String base64FullObject = Base64.getEncoder().encodeToString(sha256FullObject.digest());
                String base64ChecksumOfChecksums = Base64.getEncoder().encodeToString(sha256ChecksumOfChecksums.digest());
                if (fullObjectChecksum != null && !fullObjectChecksum.equals(base64FullObject)) {
                    throw new IOException("Failed checksum validation for full object");
                }
                System.out.println(fullObjectChecksum);
                String base64ChecksumOfChecksumFromAttributes = objectAttributes.checksum().checksumSHA256();
                if (base64ChecksumOfChecksumFromAttributes != null && !base64ChecksumOfChecksums.equals(base64ChecksumOfChecksumFromAttributes)) {
                    throw new IOException("Failed checksum validation for full object checksum of checksums");
                }
                System.out.println(base64ChecksumOfChecksumFromAttributes);
                out.flush();
            } catch (IOException | NoSuchAlgorithmException e) {
                //Cleanup bad file
                file.delete();
                e.printStackTrace();
            }
        }
     
        public static void validateExistingFileAgainstS3Checksum(S3Client s3Client) {
            System.out.println("Starting existing file validation");
            File file = new File("DOWNLOADED_" + FILE_NAME);
            GetObjectAttributesResponse
                    objectAttributes = s3Client.getObjectAttributes(GetObjectAttributesRequest.builder().bucket(BUCKET).key(FILE_NAME)
                    .objectAttributes(ObjectAttributes.OBJECT_PARTS, ObjectAttributes.CHECKSUM).build());
            try (InputStream in = new FileInputStream(file)) {
                MessageDigest sha256ChecksumOfChecksums = MessageDigest.getInstance("SHA-256");
                MessageDigest sha256Part = MessageDigest.getInstance("SHA-256");
                byte[] buffer = new byte[CHUNK_SIZE];
                int currentPart = 0;
                int partBreak = objectAttributes.objectParts().parts().get(currentPart).size();
                int totalRead = 0;
                int read = in.read(buffer);
                while (read != -1) {
                    totalRead += read;
                    if (totalRead >= partBreak) {
                        int difference = totalRead - partBreak;
                        byte[] partChecksum;
                        if (totalRead != partBreak) {
                            sha256Part.update(buffer, 0, read - difference);
                            partChecksum = sha256Part.digest();
                            sha256ChecksumOfChecksums.update(partChecksum);
                            sha256Part.reset();
                            sha256Part.update(buffer, read - difference, difference);
                        } else {
                            sha256Part.update(buffer, 0, read);
                            partChecksum = sha256Part.digest();
                            sha256ChecksumOfChecksums.update(partChecksum);
                            sha256Part.reset();
                        }
                        String base64PartChecksum = Base64.getEncoder().encodeToString(partChecksum);
                        if (!base64PartChecksum.equals(objectAttributes.objectParts().parts().get(currentPart).checksumSHA256())) {
                            throw new IOException("Part checksum didn't match S3");
                        }
                        currentPart++;
                        System.out.println(currentPart + " " + base64PartChecksum);
                        if (currentPart < objectAttributes.objectParts().totalPartsCount()) {
                            partBreak += objectAttributes.objectParts().parts().get(currentPart - 1).size();
                        }
                    } else {
                        sha256Part.update(buffer, 0, read);
                    }
                    read = in.read(buffer);
                }
                if (currentPart != objectAttributes.objectParts().totalPartsCount()) {
                    currentPart++;
                    byte[] partChecksum = sha256Part.digest();
                    sha256ChecksumOfChecksums.update(partChecksum);
                    String base64PartChecksum = Base64.getEncoder().encodeToString(partChecksum);
                    System.out.println(currentPart + " " + base64PartChecksum);
                }
     
                String base64CalculatedChecksumOfChecksums = Base64.getEncoder().encodeToString(sha256ChecksumOfChecksums.digest());
                System.out.println(base64CalculatedChecksumOfChecksums);
                System.out.println(objectAttributes.checksum().checksumSHA256());
                if (!base64CalculatedChecksumOfChecksums.equals(objectAttributes.checksum().checksumSHA256())) {
                    throw new IOException("Full object checksum of checksums don't match S3");
                }
     
            } catch (IOException | NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
        }
    }
```

------

### 使用 REST API
<a name="CheckObjectIntegrityREST"></a>

您可以發送 REST 請求以上傳具有檢查總和值的物件，以使用 [PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) 驗證資料的完整性。您也可以使用適用於 [GetObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) 或 [HeadObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html) 物件擷取檢查總和的值。

### 使用 AWS CLI
<a name="CheckObjectIntegrityCLI"></a>

您可以傳送 `PUT` 請求，以便在單一操作中上傳多達 5 GB 的物件。如需詳細資訊，請參閱*《AWS CLI 命令參考》*中的 [https://docs.aws.amazon.com/cli/latest/reference/s3api/put-object.html#examples](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-object.html#examples)。您也可以使用 [https://docs.aws.amazon.com/cli/latest/reference/s3api/get-object.html](https://docs.aws.amazon.com/cli/latest/reference/s3api/get-object.html) 和 [https://docs.aws.amazon.com/cli/latest/reference/s3api/head-object.html](https://docs.aws.amazon.com/cli/latest/reference/s3api/head-object.html) 以擷取已上傳物件的檢查總和以驗證資料的完整性。

如需相關資訊，請參閱《AWS Command Line Interface 使用者指南》**中的 [Amazon S3 CLI 常見問答集](https://docs.aws.amazon.com/cli/latest/topic/s3-faq.html)。

## 上傳物件時使用 Content-MD5
<a name="checking-object-integrity-md5"></a>

上傳後驗證物件完整性的另一種方法，是在上傳物件時提供物件的 MD5 摘要。如果要計算物件的 MD5 摘要，您可以使用 `Content-MD5` 標頭搭配 `PUT` 命令提供摘要。

上傳物件後，Amazon S3 會計算物件的 MD5 摘要，並將其與您提供的值進行比較。僅當兩個摘要匹配時，請求才會成功。

MD5 摘要不是強制需求，但您可以將其用於上傳過程以驗證物件的完整性。

## 使用 Content-MD5 和 ETag 驗證上傳的物件
<a name="checking-object-integrity-etag-and-md5"></a>

物件的實體標籤 (ETag) 表示物件的特定版本。請記住，ETag 只會反映物件內容的變更，不會反映其中繼資料的變更。如果僅變更物件的中繼資料，則 ETag 將保持不變。

根據物件的不同，物件的 ETag 可能是物件資料的 MD5 摘要：
+ 如果物件是由 `PutObject`、`PostObject`,或 `CopyObject` 操作所建立，或是通過 AWS 管理主控台，並且該物件也是採用 Amazon S3 受管金鑰 (SSE-S3) 的伺服器端加密或加密，則物件的 ETag 是該物件資料的 MD5 摘要。
+ 如果物件是由 `PutObject`、 `PostObject`或 `CopyObject`操作或透過 建立 AWS 管理主控台，且該物件是透過使用客戶提供的金鑰 (SSE-C) 的伺服器端加密或使用 AWS Key Management Service (AWS KMS) 金鑰 (SSE-KMS) 的伺服器端加密來加密，則該物件的 ETag 不是其物件資料的 MD5 摘要。
+ 如果物件是由分段上傳程序或 `UploadPartCopy` 操作所建立，則無論加密方法為何，物件的 ETag 都不會是 MD5 摘要。如果物件大於 16 MB， 會將該物件 AWS 管理主控台 上傳或複製為分段上傳，因此 ETag 不是 MD5 摘要。

對於物件 ETag 是否為 `Content-MD5` 摘要，您可以將物件的 ETag 值與計算值或先前儲存的 `Content-MD5` 摘要進行比較。

## 使用追蹤檢查總和
<a name="trailing-checksums"></a>

將大型物件上傳至 Amazon S3 時，您可以為物件提供預先計算的檢查總和，或使用 AWS SDK 來代表您自動建立區塊上傳的結尾檢查總和。如果您使用尾部檢查總和，則 Amazon S3 會使用您指定的演算法自動產生檢查總和值，以您上傳物件時，驗證分塊上傳物件的完整性。

若要在使用 AWS SDK 時建立結尾檢查總和，請使用您偏好的演算法填入 `ChecksumAlgorithm` 參數。SDK 使用該演算法計算物件 (或物件的部分) 的檢查總和，並自動將其附加到分塊上傳請求的末尾。此行為可節省您的時間，因為 Amazon S3 一次同時執行驗證和上傳您的資料。

**重要**  
如果您使用的是 S3 物件 Lambda，則對 S3 物件 Lambda 的所有請求都使用 `s3-object-lambda` 而不是 `s3`。此行為會影響追蹤檢查總和值的簽名。如需 S3 Object Lambda 的詳細資訊，請參閱 [使用 S3 Object Lambda 轉換物件](transforming-objects.md)。

### 尾部檢查總和標頭
<a name="trailing-checksums-headers"></a>

若要提出分塊內容編碼請求，Amazon S3 需要用戶端伺服器包含數個標頭，才能正確剖析請求。用戶端伺服器必須包含下列標頭：
+ **`x-amz-decoded-content-length`：**此標頭表示透過請求上傳至 Amazon S3 的實際資料的純文字大小。
+ **`x-amz-content-sha256`：**此標頭表示包含在請求中的分塊上傳類型。對於具有尾部檢查總和的分塊上傳，標頭值 `STREAMING-UNSIGNED-PAYLOAD-TRAILER` 適用於不使用承載簽署的請求，`STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER` 適用於使用 SigV4 承載簽署的請求。(如需實作已簽署承載的詳細資訊，請參閱[授權標頭的簽章計算：在多個區塊中傳輸承載](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html)。)
+ **`x-amz-trailer`：**此標頭表示請求中尾部標頭的名稱。如果存在結尾檢查總和 （其中 AWS SDKs 會將檢查總和附加到編碼的請求內文），`x-amz-trailer`標頭值會包含`x-amz-checksum-`字首，並以演算法名稱結尾。目前支援下列 `x-amz-trailer` 值：
  + `x-amz-checksum-crc32`
  + `x-amz-checksum-crc32c`
  + `x-amz-checksum-crc64nvme`
  + `x-amz-checksum-sha1`
  + `x-amz-checksum-sha256`

**注意**  
您也可以在請求中包含具有分塊值的 `Content-Encoding` 標頭。雖然不需要此標頭，但包含此標頭可以將傳輸編碼資料的 HTTP 代理問題減到最少。如果請求中存在另一個 `Content-Encoding` 標頭 (例如 gzip)，則 `Content-Encoding` 標頭會在逗號分隔的編碼清單中包含分塊值。例如 `Content-Encoding: aws-chunked, gzip`。

### 分塊部分
<a name="trailing-checksums-chunks"></a>

當您使用分塊編碼將物件上傳至 Amazon S3 時，上傳請求會包含下列區塊類型 (依列出的順序格式化)：
+ **物件主體區塊：**可以有一個、多個或零個與分塊上傳請求相關聯的主體區塊。
+ **完成區塊：**可以有一個、多個或零個與分塊上傳請求相關聯的主體區塊。
+ **尾部區塊：**尾部檢查總和會在完成區塊之後列出。僅允許一個尾部區塊。

**注意**  
 每個分塊上傳都必須以最終 CRLF (例如 `\r\n`) 結尾，以表示請求的結尾。

如需分塊格式化的範例，請參閱 [範例：使用尾部檢查總和的分塊上傳](#example-chunked-uploads-trailing)。

#### 物件主體區塊
<a name="trailing-checksums-object-body-chunks"></a>

物件主體區塊是包含要上傳至 S3 之實際物件資料的區塊。這些區塊具有一致的大小和格式限制。

##### 物件主體區塊大小
<a name="trailing-checksums-object-body-chunks-size"></a>

這些區塊必須包含至少 8,192 個位元組 (或 8 KiB) 的物件資料，但最終主體區塊除外，該區塊可以更小。沒有明確的區塊大小上限，但您可以預期所有區塊都小於 5 GB 的最大上傳項目。區塊大小可能會因用戶端伺服器實作而有所不同。

##### 物件主體區塊格式
<a name="trailing-checksums-object-body-chunks-format"></a>

物件主體區塊的開頭是物件主體區塊中位元組數的十六進位編碼，後面接著 CRLF (歸位字元換行)、該區塊的物件位元組，以及另一個 CRLF。

例如：

```
hex-encoding-of-object-bytes-in-chunk\r\n
chunk-object-bytes\r\n
```

不過，簽署區塊時，物件主體區塊會遵循不同的格式，其中簽章會以分號分隔符號附加至區塊大小。例如：

```
hex-encoding-of-object-bytes-in-chunk;chunk-signature\r\n
 chunk-object-bytes\r\n
```

如需區塊簽署的詳細資訊，請參閱[Authorization標頭的簽章計算：在多個區塊中傳輸承載 (AWS 簽章版本 4)](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html)。如需區塊格式化的詳細資訊，請參閱 *RFC 編輯器*網站上的[區塊傳輸編碼](https://www.rfc-editor.org/rfc/rfc9112#name-chunked-transfer-coding)。

#### 完成區塊
<a name="trailing-checksums-completion-chunks"></a>

完成區塊必須是每個分塊上傳的最終物件主體區塊。完成區塊的格式，相似於主體區塊，但一律包含零位元組的物件資料。(物件資料的零位元組表示所有資料都已上傳。) 分塊上傳必須包含完成區塊做為其最終物件主體區塊，格式如下所示：

```
0\r\n
```

不過，如果內容編碼請求使用承載簽署，則會改為遵循此格式：

```
0;chunk-signature\r\n
```

#### 尾部區塊
<a name="trailing-checksums-trailer-chunks"></a>

尾部區塊會保留所有 S3 上傳請求的計算檢查總和。尾部區塊包含兩個欄位：一個標頭名稱欄位和一個標頭值欄位。上傳請求的標頭名稱欄位必須符合傳入 `x-amz-trailer` 請求標頭的值。例如，若請求包含 `x-amz-trailer: x-amz-checksum-crc32`，且尾部區塊具有標頭名稱 `x-amz-checksum-sha1`，則請求會失敗。尾部區塊中的值欄位，包含該物件大尾序檢查總和值的 base64 編碼。(大尾序會將最顯著的資料位元組儲存在最低的記憶體位址，並將最不顯著的資料位元組儲存在最大的記憶體位址)。用於計算此檢查總和的演算法，與標頭名稱的字尾相同 (例如 `crc32`)。

##### 尾部區塊格式
<a name="trailing-checksums-trailer-chunk-format"></a>

尾部區塊針對未簽署的承載請求使用下列格式：

```
x-amz-checksum-lowercase-checksum-algorithm-name:base64-checksum-value\n\r\n\r\n
```

對於具有 [SigV4 簽署的承載](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming-trailers.html)請求，尾部區塊包含尾部區塊的追蹤簽章。

```
trailer-checksum\n\r\n
trailer-signature\r\n
```

您也可以直接將 CRLF 新增至 base64 檢查總和值的末尾。例如：

```
x-amz-checksum-lowercase-checksum-algorithm-name:base64-checksum-value\r\n\r\n
```

#### 範例：使用尾部檢查總和的分塊上傳
<a name="example-chunked-uploads-trailing"></a>

Amazon S3 支援分塊上傳，其可針對 `PutObject` 和 `UploadPart` 請求採用 `aws-chunked` 內容編碼，並附加尾部檢查總和。

**Example 1 – 具有尾部 CRC-32 檢查總和的未簽署分塊 `PutObject` 請求**  

 以下是具有尾部 CRC-32 檢查總和的分塊 `PutObject` 請求範例。在此範例中，用戶端會在三個未簽署區塊中上傳 17 KB 物件，並使用 `x-amz-checksum-crc32` 標頭附加尾部 CRC-32 檢查總和區塊。

```
PUT /Key+ HTTP/1.1
Host: amzn-s3-demo-bucket
Content-Encoding: aws-chunked
x-amz-decoded-content-length: 17408
x-amz-content-sha256: STREAMING-UNSIGNED-PAYLOAD-TRAILER
x-amz-trailer: x-amz-checksum-crc32

2000\r\n                                   // Object body chunk 1 (8192 bytes)
object-bytes\r\n
2000\r\n                                   // Object body chunk 2 (8192 bytes)
object-bytes\r\n
400\r\n                                    // Object body chunk 3 (1024 bytes)
object-bytes\r\n
0\r\n                                      // Completion chunk
x-amz-checksum-crc32:YABb/g==\n\r\n\r\n    // Trailer chunk (note optional \n character)
\r\n                                         // CRLF
```

此處為範例回應：

```
HTTP/1.1 200
ETag: ETag
x-amz-checksum-crc32: YABb/g==
```

**注意**  
 檢查總和值尾部對 linefeed `\n` 的使用，可能因用戶端而異。

**Example 2 – 具有尾部 CRC-32 (`CRC32`) 檢查總和的 SigV4 簽署的分塊 `PutObject` 請求**  

以下是具有尾部 CRC-32 檢查總和的分塊 `PutObject` 請求範例。此請求使用 SigV4 承載簽署。在此範例中，用戶端會在三個已簽署區塊中上傳 17 KB 物件。除了 `object body` 區塊之外，`completion chunk` 和 `trailer chunk` 也會簽署。

```
PUT /Key+ HTTP/1.1
Host: amzn-s3-demo-bucket.s3.amazonaws.com
Content-Encoding: aws-chunked
x-amz-decoded-content-length: 17408
x-amz-content-sha256: STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER
x-amz-trailer: x-amz-checksum-crc32
		
authorization-code                            // SigV4 headers authorization

2000;chunk-signature=signature-value...\r\n   // Object body chunk 1 (8192 bytes)
object-bytes\r\n
2000;chunk-signature\r\n                      // Object body chunk 2 (8192 bytes)
object-bytes\r\n
400;chunk-signature\r\n                       // Object body chunk 3 (1024 bytes)
object-bytes\r\n
0;chunk-signature\r\n                         // Completion chunk
x-amz-checksum-crc32:YABb/g==\n\r\n            // Trailer chunk (note optional \n character)
trailer-signature\r\n
\r\n                                           // CRLF
```

此處為範例回應：

```
HTTP/1.1 200
ETag: ETag
x-amz-checksum-crc32: YABb/g==
```