

# 在 Amazon S3 中检查对象完整性
<a name="checking-object-integrity"></a>

Amazon S3 在对象的存储生命周期中提供了一系列数据保护功能。通过 Amazon S3，可以使用校验和值来验证您上传或下载的数据的完整性。此外，可以请求为存储在 S3 中的任何对象计算另一个校验和值。

上传、复制或管理数据时，可以从几种支持的校验和算法中进行选择：
+ CRC-64/NVME (`CRC64NVME`)
**注意**  
`CRC64NVME` 校验和算法是用于校验和计算的默认校验和算法。
+ CRC-32 (`CRC32`)
+ CRC-32C (`CRC32C`)
+ SHA-1 (`SHA1`)
+ SHA-256 (`SHA256`)
+ MD5 (`MD5`)
**注意**  
对于分段上传，计算校验和操作使用 `MD5` 来提供完整对象校验和值，这在上传过程中是不可能的。对于单分段上传，`content-MD5 header` 仅在为对象使用 S3 ETag 时才可用，并且必须使用 SSE-S3 加密。

当将对象上传到 S3 时，可以指定使用这些校验和算法中的任何一种。对于上传，AWS 拥有的所有客户端都会计算对象的校验和，然后将其与上传请求一起发送。然后，S3 在服务器端独立计算对象的校验和值，并使用提供的值对其进行验证，然后再存储对象和校验和值。当您执行单分段上传或分段上传时（对分段上传使用完整对象校验和类型），还可以为这些校验和算法提供预先计算的值。要将预先计算的值用于多个对象，请使用 AWS CLI 或 AWS SDK。

或者，如果您想在无需还原或下载数据的情况下验证 S3 中的数据集，则可以将**计算校验和**操作与 S3 批量操作结合使用。**计算校验和**操作支持您在一个任务请求中高效地验证数十亿个对象。执行**计算校验和**操作时，S3 会为一组静态对象计算校验和值。在任务请求结束时，您将收到一份自动生成的完整性报告（也称为完成报告），您可以使用该报告来确认数据集是否完好无损。

**Topics**
+ [

# 在 Amazon S3 中检查数据上传的对象完整性
](checking-object-integrity-upload.md)
+ [

# 在 Amazon S3 中检查静态数据的对象完整性
](checking-object-integrity-at-rest.md)

# 在 Amazon S3 中检查数据上传的对象完整性
<a name="checking-object-integrity-upload"></a>

Amazon S3 在上传和下载操作过程中使用校验和值来验证数据完整性。上传数据时，AWS SDK 和 AWS 管理控制台使用您选择的校验和算法，在数据传输之前计算校验和值。然后，S3 独立计算数据的校验和，并对照提供的校验和值对其进行验证。只有在确认在传输过程中保持了数据完整性后，才会接受对象。S3 既会将校验和值存储为对象元数据，又会存储对象本身。

要验证对象完整性，可以在下载过程中请求校验和值。这种验证跨加密模式、对象大小、存储类别以及单分段上传和分段上传保持一致。要更改上传的校验和算法，可以复制单个对象，也可以对多个对象使用批量复制。

对于单分段上传，可以提供校验和值作为标头。可以提供预先计算的值，也可以让 AWS SDK 在上传期间计算一个值。如果 S3 计算出的校验和值与您提供的值相匹配，则接受请求。如果值不匹配，则拒绝请求。

对于分段上传，AWS SDK 可以自动为分块上传创建尾随校验和。使用尾随校验和时，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`)
**注意**  
对于在使用 SSE-S3 加密的单分段上传（`PUT` 操作）中上传的对象，仅当为这些对象使用 S3 ETag 时，`content-MD5` 标头才可用。

此外，您可以使用 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 批量操作](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 SDK 会将校验和传递给 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 都可以作为单个操作执行此复制。使用 copy 命令，对象的校验和是完整对象的直接校验和。如果对象最初是使用分段上传进行上传的，即使数据没有变化，校验和值也会发生变化。
+ 大于 `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 SDK
<a name="CheckObjectIntegritySDK"></a>

以下示例显示了如何使用 AWS SDK 通过分段上传来上传大文件、下载大文件和验证分段上传文件，所有这些都使用 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 FAQ](https://docs.aws.amazon.com/cli/latest/topic/s3-faq.html)。

## 在上传对象时使用 Content-M5
<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 对象 Lambda 的更多信息，请参阅 [使用 S3 对象 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`，而针对使用 SigV4 有效载荷签名的请求，标头值为 `STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER`。（有关实现签名有效载荷的更多信息，请参阅 [Signature calculations for the authorization header: Transferring a payload in multiple chunks](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html)。）
+ **`x-amz-trailer`：**此标头表示请求中尾随标头的名称。如果尾随校验和存在（其中 AWS SDK 将校验和附加到已编码的请求正文），则 `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>

这些分块必须包含至少 8192 字节（或 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
```

有关分块签名的更多信息，请参阅 [Signature calculations for the Authorization Header: Transferring a payload in multiple chunks (AWS Signature Version 4)](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html)。有关分块格式的更多信息，请参阅 *RFC 编辑器* 网站上的 [Chunked transfer encoding](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 signed payloads](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>

对于带有尾随校验和的 `PutObject` 和 `UploadPart` 请求，Amazon S3 支持使用 `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==
```

**注意**  
 校验和值末尾的换行符 `\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==
```

# 在 Amazon S3 中检查静态数据的对象完整性
<a name="checking-object-integrity-at-rest"></a>

如果您需要验证存储在 Amazon S3 中的数据集的内容，S3 批量操作[计算校验和](https://docs.aws.amazon.com//AmazonS3/latest/userguide/batch-ops-compute-checksums.html)操作会计算静态对象的完整对象或复合校验和。**计算校验和**操作使用批量操作来异步计算一组对象的校验和值，并自动生成合并的完整性报告，而无需创建数据的新副本，也无需还原或下载任何数据。

使用**计算校验和**操作，可以通过单个任务请求高效地验证数十亿个对象。对于每个**计算校验和**任务请求，S3 都会计算校验和值，并将其包含在自动生成的完整性报告（也称为完成报告）中。然后，可以使用此完成报告来验证数据集的完整性。

**计算校验和**操作适用于存储在 S3 中的任何对象，无论存储类别或对象大小如何。无论您是需要验证对象以作为数据保留最佳实践，还是需要满足合规性要求，**计算校验和**操作都可以通过执行静态校验和计算，来降低数据验证所需的成本、时间和精力。有关**计算校验和**定价的信息，请参阅 [Amazon S3 定价](https://aws.amazon.com/s3/pricing/)上的**管理与见解**选项卡。

然后，可以使用所生成的完成报告的输出来与存储在数据库中的校验和值进行比较，以验证数据集在一段时间内是否保持完好无损。这种方法有助于您保持端到端的数据完整性，以满足业务和合规性需要。例如，可以使用**计算校验和**操作来提交 S3 Glacier 存储类别中存储的一组对象，以便进行年度安全审计。此外，一系列支持的校验和算法使您能够与应用程序中使用的算法保持连续性。

## 使用支持的校验和算法
<a name="using-additional-checksums-rest"></a>

对于静态数据，可以使用任何支持的校验和算法来计算校验和：
+ CRC-64/NVME (`CRC64NVME`)：仅支持完整对象校验和类型。
+ CRC-32 (`CRC32`)：同时支持完整对象和复合校验和类型。
+ CRC-32C (`CRC32C`)：同时支持完整对象和复合校验和类型。
+ SHA-1 (`SHA1`)：同时支持完整对象和复合校验和类型。
+ SHA-256 (`SHA256`)：同时支持完整对象和复合校验和类型。
+ MD5 (`MD5`)：同时支持完整对象和复合校验和类型。

## 使用**计算校验和**
<a name="Compute-checksums"></a>

对于存储在 Amazon S3 中的对象，可以将**计算校验和**操作与 S3 批量操作结合使用，以检查存储的静态数据的内容。可以使用 Amazon S3 控制台、AWS Command Line Interface（AWS CLI）、REST API 或 AWS SDK [创建 Compute checksum 批量操作任务](https://docs.aws.amazon.com/AmazonS3/latest/userguide/batch-ops-create-job.html)。**计算校验和**任务完成后，您将收到完成报告。有关如何使用完成报告的更多信息，请参阅[跟踪任务状态和完成报告](https://docs.aws.amazon.com/AmazonS3/latest/userguide/batch-ops-job-status.html)。

在创建**计算校验和**任务之前，必须创建 S3 批量操作 AWS Identity and Access Management（IAM）角色来向 Amazon S3 授予代表您执行操作的权限。您需要授予读取清单文件和向 S3 存储桶写入完成报告的权限。有关更多信息，请参阅 [计算校验和](batch-ops-compute-checksums.md)。

### 使用 S3 控制台
<a name="Compute-checksum-console"></a>

**使用**计算校验和**操作**

1. 登录到 AWS 管理控制台，然后通过以下网址打开 Amazon S3 控制台：[https://console.aws.amazon.com/s3/](https://console.aws.amazon.com/s3/)。

1. 在页面顶部的导航栏中，选择当前所显示 AWS 区域的名称。接下来，选择要在其中创建任务的区域。
**注意**  
对于复制操作，必须在目标存储桶所在的同一区域中创建任务。对于所有其它操作，必须在与清单中的对象相同的区域中创建任务。

1. 在 Amazon S3 控制台的左侧导航窗格中选择**批量操作**。

1. 请选择**创建任务**。

1. 查看要在其中创建作业的 AWS 区域。
**注意**  
对于复制操作，必须在目标存储桶所在的同一区域中创建任务。对于所有其它操作，必须在与清单中的对象相同的区域中创建任务。

1. 在 **Manifest format (清单格式)** 下，请选择要使用的清单对象的类型。
   + 如果您选择 **S3 清单报告（manifest.json）**，请输入指向 `manifest.json` 对象的路径；（可选）如果您要使用特定的对象版本，则输入**清单对象版本 ID**。或者，可以选择**浏览 S3** 并选择清单 JSON 文件，该文件会自动填充所有清单对象字段条目。
   + 如果您选择 **CSV**，请选择清单位置类型，然后输入指向 CSV 格式的清单对象的路径，或选择**浏览 S3** 来选择清单对象。清单对象必须遵循控制台中描述的格式。如果要使用特定版本的清单对象，也可以指定对象版本 ID。
   + 如果您选择**使用 S3 复制配置创建清单**，则将使用复制配置生成对象列表，并保存到您选择的目标（可选）。使用复制配置生成清单时，唯一可用的操作是**复制**。

1. 选择**下一步**。

1. 在**操作**下，选择**计算校验和**操作，以便对清单中列出的所有对象计算校验和。为您的任务选择**校验和类型**和**校验和函数**。然后选择**下一步**。

1. 填写**配置其它选项**的信息，然后选择**下一步**。

1. 在**配置其它选项**页面上，填写**计算校验和**任务的信息。
**注意**  
在**完成报告**下，请务必确认此确认声明。此确认声明确认您了解完成报告包含校验和值，这些值用于验证存储在 Amazon S3 中的数据的完整性。因此，分享完成报告时应务必谨慎。另外，请注意，如果您正在创建计算校验和请求并指定外部账户拥有者的存储桶位置来存储完成报告，请务必指定外部存储桶拥有者的 AWS 账户 ID。

1. 选择**下一步**。

1. 在**审核**页面上，审核并确认设置。

1. （可选）如果需要进行更改，请选择**上一步**以返回上一页，或者选择**编辑**以更新特定的步骤。

1. 确认更改后，选择**创建任务**。

**列出并监控所有**计算校验和**请求的进度**

1. 登录到 AWS 管理控制台，然后通过以下网址打开 Amazon S3 控制台：[https://console.aws.amazon.com/s3/](https://console.aws.amazon.com/s3/)。

1. 在左侧导航窗格中，选择**批量操作**。

1. 在**批量操作**页面上，可以查看任务详细信息，例如任务优先级、任务完成率和对象总数。

1. 如果要管理或克隆特定的**计算校验和**任务，请单击**任务 ID** 以查看其它任务信息。

1. 在特定的**计算校验和**任务页面上，查看任务详细信息。

每个批量操作任务都会逐步经历不同的[任务状态](https://docs.aws.amazon.com/AmazonS3/latest/userguide/batch-ops-job-status.html#batch-ops-job-status-table)。还可以在 S3 控制台中[启用 AWS CloudTrail 事件](https://docs.aws.amazon.com/AmazonS3/latest/userguide/enable-cloudtrail-logging-for-s3.html)，以接收有关任何任务状态变化的提醒。对于处于活动状态的任务，可以在**任务详细信息**页面上查看正在运行的任务和完成率。

### 使用 AWS SDK
<a name="Compute-checksum-sdk"></a>

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

**Example 示例：创建**计算校验和**任务**  
以下示例向您展示了如何创建**计算校验和**任务（作为**创建任务**请求的一部分），以及如何指定清单：  

```
// Required parameters
String accountId = "111122223333";
String roleArn = "arn:aws:iam::111122223333:role/BatchOperations";
String manifestArn = "arn:aws:s3:::my_manifests/manifest.csv";
String manifestEtag = "60e460c9d1046e73f7dde5043ac3ae85";
String reportBucketArn = "arn:aws:s3:::amzn-s3-demo-completion-report-bucket";
String reportExpectedBucketOwner = "111122223333";
String reportPrefix = "demo-report";

// Job Operation
S3ComputeObjectChecksumOperation s3ComputeObjectChecksum = S3ComputeObjectChecksumOperation.builder()
    .checksumAlgorithm(ComputeObjectChecksumAlgorithm.CRC64)
    .checksumType(ComputeObjectChecksumType.COMPOSITE)
    .build();

JobOperation operation = JobOperation.builder()
    .s3ComputeObjectChecksum(s3ComputeObjectChecksum)
    .build();

// Job Manifest
JobManifestLocation location = JobManifestLocation.builder()
    .eTag(manifestEtag)
    .objectArn(manifestArn)
    .build();

JobManifestSpec spec = JobManifestSpec.builder()
    .format(JobManifestFormat.S3_BATCH_OPERATIONS_CSV_20180820)
    .fields(Arrays.asList(JobManifestFieldName.BUCKET, JobManifestFieldName.KEY))
    .build();

JobManifest manifest = JobManifest.builder()
    .location(location)
    .spec(spec)
    .build();

// Completion Report
JobReport report = JobReport.builder()
    .bucket(reportBucketArn)
    .enabled(true) // Must be true
    .expectedBucketOwner(reportExpectedBucketOwner)
    .format(JobReportFormat.REPORT_CSV_20180820)
    .prefix(reportPrefix)
    .reportScope(JobReportScope.ALL_TASKS)
    .build();

// Create Job Request
CreateJobRequest request = CreateJobRequest.builder()
    .accountId(accountId)
    .confirmationRequired(false)
    .manifest(manifest)
    .operation(operation)
    .priority(10)
    .report(report)
    .roleArn(roleArn);

// Create the client
S3ControlClient client = S3ControlClient.builder()
    .credentialsProvider(new ProfileCredentialsProvider())
    .region(Region.US_EAST_1)
    .build();

// Send the request
try {
    CreateJobResponse response = client.createJob(request);
    System.out.println(response);    
} catch (AwsServiceException e) {
    System.out.println("AwsServiceException: " + e.getMessage());
    throw new RuntimeException(e);
} catch (SdkClientException e) {
    System.out.println("SdkClientException: " + e.getMessage());
    throw new RuntimeException(e);
}
```

**Example 示例：查看**计算校验和**任务详细信息**  
以下示例说明如何指定任务 ID 以查看**计算校验和**任务请求的任务详细信息（例如任务完成率）：  

```
DescribeJobRequest request = DescribeJobRequest.builder()
        .accountId("1234567890")
        .jobId("a16217a1-e082-48e5-b04f-31fac3a66b13")
        .build();
```

------

### 使用 AWS CLI
<a name="Compute-checksum-cli"></a>

可以使用 [https://docs.aws.amazon.com/cli/latest/reference/s3control/create-job.html](https://docs.aws.amazon.com/cli/latest/reference/s3control/create-job.html) 命令来创建新的批量操作任务并提供对象的列表。然后，指定校验和算法和校验和类型，以及要在其中保存**计算校验和**报告的目标存储桶。以下示例通过使用 S3 为 AWS 账户 *111122223333* 生成的清单来创建 S3 批量操作**计算校验和**任务。

要使用此命令，请将*用户输入占位符*替换为您自己的信息：

```
aws s3control create-job \
    --account-id 111122223333 \
    --manifest '{"Spec":{"Format":"S3BatchOperations_CSV_20180820","Fields":["Bucket","Key"]},"Location":{"ObjectArn":"arn:aws:s3:::my-manifest-bucket/manifest.csv","ETag":"e0e8bfc50e0f0c5d5a1a5f0e0e8bfc50"}}' \
    --manifest-generator '{
        "S3JobManifestGenerator": {
          "ExpectedBucketOwner": "111122223333",
          "SourceBucket": "arn:aws:s3:::amzn-s3-demo-source-bucket",
          "EnableManifestOutput": true,
          "ManifestOutputLocation": {
            "ExpectedManifestBucketOwner": "111122223333",
            "Bucket": "arn:aws:s3:::amzn-s3-demo-manifest-bucket",
            "ManifestPrefix": "prefix",
            "ManifestFormat": "S3InventoryReport_CSV_20211130"
          },
          "Filter": {
            "CreatedAfter": "2023-09-01",
            "CreatedBefore": "2023-10-01",
            "KeyNameConstraint": {
              "MatchAnyPrefix": [
                "prefix"
              ],
              "MatchAnySuffix": [
                "suffix"
              ]
            },
            "ObjectSizeGreaterThanBytes": 100,
            "ObjectSizeLessThanBytes": 200,
            "MatchAnyStorageClass": [
              "STANDARD",
              "STANDARD_IA"
            ]
          }
        }
      }' \
    --operation '{"S3ComputeObjectChecksum":{"ChecksumAlgorithm":"CRC64NVME","ChecksumType":"FULL_OBJECT"}}' \
    --report '{"Bucket":"arn:aws:s3:::my-report-bucket","Format":"Report_CSV_20180820","Enabled":true,"Prefix":"batch-op-reports/","ReportScope":"AllTasks","ExpectedBucketOwner":"111122223333"}' \
    --priority 10 \
    --role-arn arn:aws:iam::123456789012:role/S3BatchJobRole \
    --client-request-token 6e023a7e-4820-4654-8c81-7247361aeb73 \
    --description "Compute object checksums" \
    --region us-west-2
```

提交**计算校验和**任务后，您将收到作为响应的任务 ID，它会显示在 S3 批量操作列表页面上。Amazon S3 处理对象列表并计算每个对象的校验和。任务完成后，S3 在指定的目标处提供合并的**计算校验和**报告。

要监控**计算校验和**任务的进度，请使用 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3control/describe-job.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3control/describe-job.html) 命令。此命令检查指定的批量操作任务的状态。要使用此命令，请将*用户输入占位符*替换为您自己的信息。

例如：

```
aws s3control describe-job --account-id 111122223333 --job-id 1234567890abcdef0
```

要[列出](https://docs.aws.amazon.com/AmazonS3/latest/userguide/batch-ops-list-jobs.html)所有**活动**和**完成**的批量操作任务，请参阅[列出任务](https://docs.aws.amazon.com/AmazonS3/latest/userguide/batch-ops-list-jobs.html)或《AWS CLI Command Reference》**中的 [list-jobs](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3control/list-jobs.html)。

### 使用 REST API
<a name="Compute-checksum-api"></a>

可以发送 REST 请求，以使用 [CreateJob](https://docs.aws.amazon.com/AmazonS3/latest/API/API_control_CreateJob.html) 通过**计算校验和**来验证对象完整性。可以通过向 [DescribeJob](https://docs.aws.amazon.com/AmazonS3/latest/API/API_control_DescribeJob.html) API 操作发送 REST 请求来监控**计算校验和**请求的进度。每个批量操作任务都将逐步经历以下状态：
+ **新**
+ **正在准备**
+ **就绪**
+ **ACTIVE**
+ **正在暂停**
+ **已暂停**
+ **完成**
+ **正在取消**
+ **已失败**

API 响应会通知您当前的任务状态。