

 **此页面仅适用于使用文件库和 2012 年原始 REST API 的 Amazon Glacier 服务的现有客户。**

如果您正在寻找归档存储解决方案，建议使用 Amazon S3 中的 Amazon Glacier 存储类别 S3 Glacier Instant Retrieval、S3 Glacier Flexible Retrieval 和 S3 Glacier Deep Archive。要了解有关这些存储选项的更多信息，请参阅 [Amazon Glacier 存储类别](https://aws.amazon.com/s3/storage-classes/glacier/)。

Amazon Glacier（最初基于保管库的独立服务）不再接受新客户。Amazon Glacier 是一项独立的服务 APIs ，拥有自己的服务，可将数据存储在文件库中，不同于亚马逊 S3 和 Amazon S3 Glacier 存储类别。在 Amazon Glacier 中，您现有的数据将确保安全，并且可以无限期地访问。无需进行迁移。对于低成本、长期的存档存储， AWS 建议[使用 Amazon S3 Glacier 存储类别，这些存储类别](https://aws.amazon.com/s3/storage-classes/glacier/)基于S3存储桶 APIs、完全 AWS 区域 可用性、更低的成本和 AWS 服务集成，可提供卓越的客户体验。如果您希望加强功能，可以考虑使用我们的 [AWS 将数据从 Amazon Glacier 文件库传输到 Amazon S3 Glacier 存储类别的解决方案指南](https://aws.amazon.com/solutions/guidance/data-transfer-from-amazon-s3-glacier-vaults-to-amazon-s3/)，迁移到 Amazon S3 Glacier 存储类别。

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 计算校验和
<a name="checksum-calculations"></a>

上传档案时，您必须包括 `x-amz-sha256-tree-hash` 和 `x-amz-content-sha256` 标头。`x-amz-sha256-tree-hash` 标头是您的请求正文中有效载荷的校验和。此主题描述了如何计算 `x-amz-sha256-tree-hash` 标头。`x-amz-content-sha256` 标头是整个有效载荷的哈希，并且是授权所必需的项目。有关更多信息，请参阅[流式处理 API 的示例签名计算](amazon-glacier-signing-requests.md#example-signature-calculation-streaming)。

您的请求的有效载荷可以是：

 
+ **整个档案** – 在单一请求中使用上传档案 API 上传档案时，您可以在请求正文中发送整个档案。在这种情况下，您必须包括整个档案的校验和。
+ **档案段** – 使用分段上传 API 分段上传档案时，您可以在请求正文中只发送档案的一段。在这种情况下，您可以包括档案段的校验和。上传所有段后，您可以发送完成分段上传请求，该请求必须包括整个档案的校验和。

有效载荷的校验和为 SHA256 树形哈希。它被称为树形哈希是因为，在计算校验和的过程中，您会计算 SHA256 哈希值树。根部的哈希值为整个档案的校验和。

 

**注意**  
此部分描述了一种计算 SHA256 树形哈希的方法。但是，只要能得出相同的结果，您可以使用任何方法。

您可以按以下方法计算 SHA256 树形哈希：

 

1. 针对有效载荷数据的每个 1 MB 分块，计算 SHA256 哈希。数据的最后一个分块可以小于 1 MB。例如，如果您要上传一个 3.2 MB 的档案，则您需要计算数据的前三个 1 MB 分块中每一个分块的 SHA256 哈希值，然后计算剩余 0.2 MB 数据的 SHA256 哈希。这些哈希值构成了树的叶节点。

1. 构建树的下一层。

   1. 连接两个连续子节点的哈希值，然后计算连接的哈希值的 SHA256 哈希。此连接和 SHA256 哈希的生成会产生这两个子节点的父节点。

   1. 如果只剩下一个子节点，您可以将该哈希值提升到树的下一层。

1. 重复步骤 2，直到结果树具有根为止。树根提供了整个档案的哈希，相应的子树根提供了分段上传中的段的哈希。

**Topics**
+ [树形哈希示例 1：在单一请求中上传档案](#checksum-calculations-upload-archive-in-single-payload)
+ [树形哈希示例 2：使用分段上传来上传档案](#checksum-calculations-upload-archive-using-mpu)
+ [计算文件的树形哈希](#checksum-calculations-examples)
+ [下载数据时接收校验和](checksum-calculations-range.md)

## 树形哈希示例 1：在单一请求中上传档案
<a name="checksum-calculations-upload-archive-in-single-payload"></a>

在单一请求中使用上传档案 API（请参阅[上传档案（POST archive）](api-archive-post.md)）上传档案时，请求的有效载荷会包括整个档案。因此，您必须在 `x-amz-sha256-tree-hash` 请求标头中包括整个档案的树形哈希。假设您要上传一个 6.5 MB 的档案。下图说明了创建档案的 SHA256 哈希的流程。您读取档案并为每个 1 MB 分块计算 SHA256 哈希。此外，您还要为剩余的 0.5 MB 数据计算哈希，然后按前面的步骤所述构建树。

 

![\[示意图显示树形哈希示例在单一请求中上传档案。\]](http://docs.aws.amazon.com/zh_cn/amazonglacier/latest/dev/images/TreeHash-ArchiveUploadSingleRequest.png)


## 树形哈希示例 2：使用分段上传来上传档案
<a name="checksum-calculations-upload-archive-using-mpu"></a>

在使用分段上传来上传档案时计算树形哈希的流程与在单一请求中上传档案时相同。唯一的区别是，在分段上传中，您在每个请求中只（使用[上传段（PUT uploadID）](api-upload-part.md)API）上传档案的一段，因此，您在 `x-amz-sha256-tree-hash` 请求标头中只提供该段的校验和。但是，上传所有段后，您必须发送完成分段上传（请参阅[完成分段上传（POST uploadID）](api-multipart-complete-upload.md)）请求，并在 `x-amz-sha256-tree-hash` 请求标头中包含整个档案的树形哈希。

 

![\[示意图显示树形哈希示例使用分段上传来上传档案。\]](http://docs.aws.amazon.com/zh_cn/amazonglacier/latest/dev/images/TreeHash-MPU.png)


## 计算文件的树形哈希
<a name="checksum-calculations-examples"></a>

此处显示的算法是出于演示目的而选择的。您可以根据实施情况的需要而优化代码。如果您使用 Amazon SDK 对 Amazon Glacier（Amazon Glacier）进行编程，则系统会为您完成树形哈希计算，您只需提供文件引用。

**Example 1：Java 示例**  
以下示例说明如何使用 Java 计算文件的 SHA256 树形哈希。您可以通过提供文件位置作为参数来运行此示例，也可以直接从您的代码使用 `TreeHashExample.computeSHA256TreeHash` 方法。  

```
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class TreeHashExample {

static final int ONE_MB = 1024 * 1024;

    /**
     * Compute the Hex representation of the SHA-256 tree hash for the specified
     * File
     * 
     * @param args
     *            args[0]: a file to compute a SHA-256 tree hash for
     */
    public static void main(String[] args) {

        if (args.length < 1) {
            System.err.println("Missing required filename argument");
            System.exit(-1);
        }

        File inputFile = new File(args[0]);
        try {

            byte[] treeHash = computeSHA256TreeHash(inputFile);
            System.out.printf("SHA-256 Tree Hash = %s\n", toHex(treeHash));

        } catch (IOException ioe) {
            System.err.format("Exception when reading from file %s: %s", inputFile,
                    ioe.getMessage());
            System.exit(-1);

        } catch (NoSuchAlgorithmException nsae) {
            System.err.format("Cannot locate MessageDigest algorithm for SHA-256: %s",
                    nsae.getMessage());
            System.exit(-1);
        }
    }

    /**
     * Computes the SHA-256 tree hash for the given file
     * 
     * @param inputFile
     *            a File to compute the SHA-256 tree hash for
     * @return a byte[] containing the SHA-256 tree hash
     * @throws IOException
     *             Thrown if there's an issue reading the input file
     * @throws NoSuchAlgorithmException
     */
    public static byte[] computeSHA256TreeHash(File inputFile) throws IOException,
            NoSuchAlgorithmException {

        byte[][] chunkSHA256Hashes = getChunkSHA256Hashes(inputFile);
        return computeSHA256TreeHash(chunkSHA256Hashes);
    }

    /**
     * Computes a SHA256 checksum for each 1 MB chunk of the input file. This
     * includes the checksum for the last chunk even if it is smaller than 1 MB.
     * 
     * @param file
     *            A file to compute checksums on
     * @return a byte[][] containing the checksums of each 1 MB chunk
     * @throws IOException
     *             Thrown if there's an IOException when reading the file
     * @throws NoSuchAlgorithmException
     *             Thrown if SHA-256 MessageDigest can't be found
     */
    public static byte[][] getChunkSHA256Hashes(File file) throws IOException,
            NoSuchAlgorithmException {

        MessageDigest md = MessageDigest.getInstance("SHA-256");

        long numChunks = file.length() / ONE_MB;
        if (file.length() % ONE_MB > 0) {
            numChunks++;
        }

        if (numChunks == 0) {
            return new byte[][] { md.digest() };
        }

        byte[][] chunkSHA256Hashes = new byte[(int) numChunks][];
        FileInputStream fileStream = null;

        try {
            fileStream = new FileInputStream(file);
            byte[] buff = new byte[ONE_MB];

            int bytesRead;
            int idx = 0;
            int offset = 0;

            while ((bytesRead = fileStream.read(buff, offset, ONE_MB)) > 0) {
                md.reset();
                md.update(buff, 0, bytesRead);
                chunkSHA256Hashes[idx++] = md.digest();
                offset += bytesRead;
            }

            return chunkSHA256Hashes;

        } finally {
            if (fileStream != null) {
                try {
                    fileStream.close();
                } catch (IOException ioe) {
                    System.err.printf("Exception while closing %s.\n %s", file.getName(),
                            ioe.getMessage());
                }
            }
        }
    }

    /**
     * Computes the SHA-256 tree hash for the passed array of 1 MB chunk
     * checksums.
     * 
     * This method uses a pair of arrays to iteratively compute the tree hash
     * level by level. Each iteration takes two adjacent elements from the
     * previous level source array, computes the SHA-256 hash on their
     * concatenated value and places the result in the next level's destination
     * array. At the end of an iteration, the destination array becomes the
     * source array for the next level.
     * 
     * @param chunkSHA256Hashes
     *            An array of SHA-256 checksums
     * @return A byte[] containing the SHA-256 tree hash for the input chunks
     * @throws NoSuchAlgorithmException
     *             Thrown if SHA-256 MessageDigest can't be found
     */
    public static byte[] computeSHA256TreeHash(byte[][] chunkSHA256Hashes)
            throws NoSuchAlgorithmException {

        MessageDigest md = MessageDigest.getInstance("SHA-256");

        byte[][] prevLvlHashes = chunkSHA256Hashes;

        while (prevLvlHashes.length > 1) {

            int len = prevLvlHashes.length / 2;
            if (prevLvlHashes.length % 2 != 0) {
                len++;
            }

            byte[][] currLvlHashes = new byte[len][];

            int j = 0;
            for (int i = 0; i < prevLvlHashes.length; i = i + 2, j++) {

                // If there are at least two elements remaining
                if (prevLvlHashes.length - i > 1) {

                    // Calculate a digest of the concatenated nodes
                    md.reset();
                    md.update(prevLvlHashes[i]);
                    md.update(prevLvlHashes[i + 1]);
                    currLvlHashes[j] = md.digest();

                } else { // Take care of remaining odd chunk
                    currLvlHashes[j] = prevLvlHashes[i];
                }
            }

            prevLvlHashes = currLvlHashes;
        }

        return prevLvlHashes[0];
    }

    /**
     * Returns the hexadecimal representation of the input byte array
     * 
     * @param data
     *            a byte[] to convert to Hex characters
     * @return A String containing Hex characters
     */
    public static String toHex(byte[] data) {
        StringBuilder sb = new StringBuilder(data.length * 2);

        for (int i = 0; i < data.length; i++) {
            String hex = Integer.toHexString(data[i] & 0xFF);

            if (hex.length() == 1) {
                // Append leading zero.
                sb.append("0");
            }
            sb.append(hex);
        }
        return sb.toString().toLowerCase();
    }
}
```

**Example 2：C\$1 .NET 示例**  
以下示例说明如何计算文件的 SHA256 树形哈希。您可以通过提供文件位置作为参数来运行此示例。  

```
using System;
using System.IO;

using System.Security.Cryptography;

namespace ExampleTreeHash
{
    class Program
    {
        static int ONE_MB = 1024 * 1024;

        /**
        * Compute the Hex representation of the SHA-256 tree hash for the
        * specified file
        * 
        * @param args
        *            args[0]: a file to compute a SHA-256 tree hash for
        */
        public static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("Missing required filename argument");
                Environment.Exit(-1);
            }
            FileStream inputFile = File.Open(args[0], FileMode.Open, FileAccess.Read);
            try
            {
                byte[] treeHash = ComputeSHA256TreeHash(inputFile);
                Console.WriteLine("SHA-256 Tree Hash = {0}", BitConverter.ToString(treeHash).Replace("-", "").ToLower());
                Console.ReadLine();
                Environment.Exit(-1);
            }
            catch (IOException ioe)
            {
                Console.WriteLine("Exception when reading from file {0}: {1}",
                    inputFile, ioe.Message);
                Console.ReadLine();
                Environment.Exit(-1);
            }
            catch (Exception e)
            {
                Console.WriteLine("Cannot locate MessageDigest algorithm for SHA-256: {0}",
                    e.Message);
                Console.WriteLine(e.GetType());
                Console.ReadLine();
                Environment.Exit(-1);
            }
            Console.ReadLine();
        }


        /**
         * Computes the SHA-256 tree hash for the given file
         * 
         * @param inputFile
         *            A file to compute the SHA-256 tree hash for
         * @return a byte[] containing the SHA-256 tree hash
         */
        public static byte[] ComputeSHA256TreeHash(FileStream inputFile)
        {
            byte[][] chunkSHA256Hashes = GetChunkSHA256Hashes(inputFile);
            return ComputeSHA256TreeHash(chunkSHA256Hashes);
        }


        /**
         * Computes a SHA256 checksum for each 1 MB chunk of the input file. This
         * includes the checksum for the last chunk even if it is smaller than 1 MB.
         * 
         * @param file
         *            A file to compute checksums on
         * @return a byte[][] containing the checksums of each 1MB chunk
         */
        public static byte[][] GetChunkSHA256Hashes(FileStream file)
        {
            long numChunks = file.Length / ONE_MB;
            if (file.Length % ONE_MB > 0)
            {
                numChunks++;
            }

            if (numChunks == 0)
            {
                return new byte[][] { CalculateSHA256Hash(null, 0) };
            }
            byte[][] chunkSHA256Hashes = new byte[(int)numChunks][];

            try
            {
                byte[] buff = new byte[ONE_MB];

                int bytesRead;
                int idx = 0;

                while ((bytesRead = file.Read(buff, 0, ONE_MB)) > 0)
                {
                    chunkSHA256Hashes[idx++] = CalculateSHA256Hash(buff, bytesRead);
                }
                return chunkSHA256Hashes;
            }
            finally
            {
                if (file != null)
                {
                    try
                    {
                        file.Close();
                    }
                    catch (IOException ioe)
                    {
                        throw ioe;
                    }
                }
            }

        }

        /**
         * Computes the SHA-256 tree hash for the passed array of 1MB chunk
         * checksums.
         * 
         * This method uses a pair of arrays to iteratively compute the tree hash
         * level by level. Each iteration takes two adjacent elements from the
         * previous level source array, computes the SHA-256 hash on their
         * concatenated value and places the result in the next level's destination
         * array. At the end of an iteration, the destination array becomes the
         * source array for the next level.
         * 
         * @param chunkSHA256Hashes
         *            An array of SHA-256 checksums
         * @return A byte[] containing the SHA-256 tree hash for the input chunks
         */
        public static byte[] ComputeSHA256TreeHash(byte[][] chunkSHA256Hashes)
        {
            byte[][] prevLvlHashes = chunkSHA256Hashes;
            while (prevLvlHashes.GetLength(0) > 1)
            {

                int len = prevLvlHashes.GetLength(0) / 2;
                if (prevLvlHashes.GetLength(0) % 2 != 0)
                {
                    len++;
                }

                byte[][] currLvlHashes = new byte[len][];

                int j = 0;
                for (int i = 0; i < prevLvlHashes.GetLength(0); i = i + 2, j++)
                {

                    // If there are at least two elements remaining
                    if (prevLvlHashes.GetLength(0) - i > 1)
                    {

                        // Calculate a digest of the concatenated nodes
                        byte[] firstPart = prevLvlHashes[i];
                        byte[] secondPart = prevLvlHashes[i + 1];
                        byte[] concatenation = new byte[firstPart.Length + secondPart.Length];
                        System.Buffer.BlockCopy(firstPart, 0, concatenation, 0, firstPart.Length);
                        System.Buffer.BlockCopy(secondPart, 0, concatenation, firstPart.Length, secondPart.Length);

                        currLvlHashes[j] = CalculateSHA256Hash(concatenation, concatenation.Length);

                    }
                    else
                    { // Take care of remaining odd chunk
                        currLvlHashes[j] = prevLvlHashes[i];
                    }
                }

                prevLvlHashes = currLvlHashes;
            }

            return prevLvlHashes[0];
        }

        public static byte[] CalculateSHA256Hash(byte[] inputBytes, int count)
        {
            SHA256 sha256 = System.Security.Cryptography.SHA256.Create();
            byte[] hash = sha256.ComputeHash(inputBytes, 0, count);
            return hash;
        }
    }
}
```

# 下载数据时接收校验和
<a name="checksum-calculations-range"></a>

当您使用启动任务 API（请参阅[启动任务（POST jobs）](api-initiate-job-post.md)）检索档案时，您可以选择性地指定要检索的档案范围。类似地，当您使用获取任务输出 API（请参阅[获取任务输出（GET output）](api-job-output-get.md)）下载您的数据时，您可以选择性地指定要下载的数据范围。这些范围有两个特征，您在检索和下载档案的数据时务必要了解这两个特征。要检索的范围必须与档案*以兆字节对齐*。为了在下载数据时接收校验和值，要检索的范围与要下载的范围必须*以树形哈希对齐*。这两种类型的范围对齐的定义如下：

 
+ 兆字节对齐-当范围 [*StartByte*, *EndBytes*] 可被 1 MB 整除且*EndBytes*加 1 可被 1 MB 整除或等于指定存档的末尾（存档字节大小减去 1）时 *StartBytes*，则以兆字节 (1024\$11024) 对齐。启动任务 API 中使用的范围（如果指定了范围）必须以兆字节对齐。
+ Tree-hash 对齐-当且仅当在该范围内构建的树形哈希的根等同于整个档案的树形哈希中的节点时，范围 [*StartBytes*，*EndBytes*] 才与档案进行树形哈希对齐。为了接收您下载的数据的校验和值，要检索的范围以及要下载的范围必须以树形哈希对齐。有关范围以及它们与档案树形哈希的关系的示例，请参阅[树形哈希示例：检索以树形哈希对齐的档案范围](#checksum-calculations-upload-archive-with-ranges)。

  请注意，以树形哈希对齐的范围也是以兆字节对齐的。但是，以兆字节对齐的范围却不一定是以树形哈希对齐的。

以下案例描述了您在下载档案数据时，会在哪些情况下收到校验和值：

 
+ 如果您在启动任务请求中未指定要检索的范围，并且在获取任务请求中下载整个档案。
+ 如果您在启动任务请求中未指定要检索的范围，并且在获取任务请求中指定要下载的以树形哈希对齐的范围。
+ 如果您在启动任务请求中指定要检索的以树形哈希对齐的范围，并且在获取任务请求中下载整个范围。
+ 如果您在启动任务请求中指定要检索的以树形哈希对齐的范围，并且在获取任务请求中指定要下载的以树形哈希对齐的范围。

如果您在启动任务请求中指定要检索的范围，而该范围未以树形哈希对齐，则您在获取任务请求中下载数据时，仍然可以获取档案数据，但系统却不会返回校验和值。

## 树形哈希示例：检索以树形哈希对齐的档案范围
<a name="checksum-calculations-upload-archive-with-ranges"></a>

假设您的文件库中有一个 6.5 MB 的档案，您要检索该档案的 2 MB。您在启动任务请求中指定 2 MB 范围的方式决定了您在下载数据时是否会收到数据校验和值。下图说明了对于 6.5 MB 档案您可以下载的两个 2 MB 范围。这两个范围均以兆字节对齐，但只有一个范围以树形哈希对齐。

 

![\[示意图显示检索以树形哈希对齐的档案范围。\]](http://docs.aws.amazon.com/zh_cn/amazonglacier/latest/dev/images/TreeHash-ArchiveWithRanges.png)


## 以树形哈希对齐的范围说明
<a name="tree-hash-algorithm"></a>

此部分对构成以树形哈希对齐的范围的内容给出了确切的说明。您在下载档案的一部分并且指定要检索的数据范围以及要从检索的数据下载的范围时，以树形哈希对齐的范围非常重要。如果这两个范围均以树形哈希对齐，则您在下载数据时将收到校验和数据。

当且仅当在范围 [*A*, *B*]之上构建新的树形哈希，且该范围的树形哈希的根相当于整个档案的树形哈希中的一个节点时，范围 [*A*, *B*] 才相对于档案是*以树形哈希对齐的*。您可以看到[树形哈希示例：检索以树形哈希对齐的档案范围](#checksum-calculations-upload-archive-with-ranges)中的图表显示了这一点。在此部分中，我们提供了树形哈希对齐的说明。

可将 [*P*, *Q*) 视为 *N* 兆字节（MB）的档案的范围查询，而 *P* 和 *Q* 是 1 MB 的倍数。请注意，实际包括范围为 [*P* MB, *Q* MB – 1 字节]，但为了简单起见，我们将它显示为 [*P*, *Q*)。基于这些考虑因素得知：

 
+ 如果 *P* 为奇数，则只有一个可能的以树形哈希对齐的范围即，[*P*, *P* \$1 1 MB)。
+ 如果 *P* 是偶数，*k* 是最大数，其中 *P* 可以写成 2*k* \$1 *X*，则最多有 *k* 个以 *P* 开头的树形哈希对齐的范围。 *X* 是大于 0 的整数。以树形哈希对齐的范围分为以下类别：
  + 对于每个 *i*，其中（0 <= *i* <= *k*）并且 *P* \$1 2*i* < *N*，则 [*P*, *Q* \$1 2*i*) 是以树形哈希对齐的范围。
  + *P* = 0 是 *A* = 2[lgN]\$10 时的特殊情况