

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

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

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

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

# 在 Amazon Glacier 中下载档案
下载档案

Amazon Glacier 提供了一个管理控制台，您可以使用它来创建和删除文件库。但是，您无法通过使用管理控制台从 Amazon Glacier 下载档案。要下载数据（例如照片、视频和其他文档），必须使用 Amazon Command Line Interface (Amazon CLI) 或编写代码来发出请求，方法是直接使用 REST API 或使用 Amazon SDKs。

有关将 Amazon Glacier 与配合使用的信息 Amazon CLI，请参阅《[亚马逊冰川Amazon CLI 参考资料](https://docs.amazonaws.cn/cli/latest/reference/glacier/index.html)》。要安装 Amazon CLI，请参阅[Amazon Command Line Interface](https://www.amazonaws.cn/cli/)。以下主题介绍如何使用 适用于 Java 的 Amazon SDK、和 Amazon Glacier REST API 将 适用于 .NET 的 Amazon SDK档案下载到亚马逊 Glacier。

**Topics**
+ [

# 检索 Amazon Glacier 档案
](downloading-an-archive-two-steps.md)
+ [

# 使用 Amazon Glacier 下载档案 适用于 Java 的 Amazon SDK
](downloading-an-archive-using-java.md)
+ [

# 使用 Amazon Glacier 下载档案 适用于 .NET 的 Amazon SDK
](downloading-an-archive-using-dotnet.md)
+ [

# 使用 Python 并行处理下载大型档案
](downloading-large-archive-parallel-python.md)
+ [

# 使用 REST API 下载档案
](downloading-an-archive-using-rest.md)
+ [

# 使用 Amazon Glacier 下载档案 Amazon CLI
](downloading-an-archive-using-cli.md)

# 检索 Amazon Glacier 档案
检索档案

从 Amazon Glacier 检索档案是一个异步操作，您首先需要启动任务，然后在任务完成后下载输出。要启动档案取回任务，您可以使用 [启动任务（POST jobs）](api-initiate-job-post.md) REST API 操作或中的等效操作 Amazon CLI、或 Amazon SDKs。

**Topics**
+ [

## 档案检索选项
](#api-downloading-an-archive-two-steps-retrieval-options)
+ [

## 关于限范围的档案检索
](#downloading-an-archive-range)

从 Amazon Glacier 检索档案是一个分为两个步骤的过程。下面是此过程的概述：

**检索档案**

1. 启动档案检索任务。

   1. 获得您要检索的档案的 ID。您可以从文件库清单获取档案 ID。您可以通过 REST API 获取档案 ID Amazon CLI、或 Amazon SDKs。有关更多信息，请参阅 [在 Amazon Glacier 中下载文件库清单](vault-inventory.md)。

   1. 使用[启动任务（POST jobs）](api-initiate-job-post.md)操作启动任务，请求 Amazon Glacier 为后续下载准备整个档案或档案的一部分。

   当您启动任务时，Amazon Glacier 会在响应中返回任务 ID 并异步运行任务。（如步骤 2 所述，在任务完成之前，您不能下载任务输出。）
**重要**  
数据检索策略可能导致您的 `Initiate Job` 请求失败，并发生 `PolicyEnforcedException` 异常，但这仅限于标准检索。有关数据检索策略的更多信息，请参阅 [Amazon Glacier 数据检索策略](data-retrieval-policy.md)。有关 `PolicyEnforcedException` 异常的更多信息，请参阅[错误响应](api-error-responses.md)。

   如果需要，您可以还原存储在 Amazon Glacier 中的大型数据段。有关从 Amazon Glacier 存储类别恢复数据的更多信息，请参阅《Amazon Simple Storage Service 用户指南》中的[用于归档对象的存储类别]( https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html#sc-glacier)**。

1. 在任务完成后，使用[获取任务输出（GET output）](api-job-output-get.md)操作下载字节。

   您可以下载所有字节，或者指定字节范围，只下载任务输出的一部分。对于较大的输出，以区块下载输出的方式在下载失败（例如，由于网络发生故障而失败）时对您有所帮助。如果您在单一请求中获取任务输出，并且网络发生故障，则您不得不从头重新开始下载输出。但是，如果您以区块下载输出，万一发生任何故障，则您只需重新开始下载较小的部分，而不是整个输出。

Amazon Glacier 必须先完成任务，然后，您才能获取其输出。任务在完成后的至少 24 小时内都不会过期，这意味着，您可以在任务完成后的 24 小时期限内下载输出。还原可以在任务完成 24 小时后随时过期。要确定您的任务是否已完成，请使用以下选项之一检查其状态：
+ **等待任务完成通知** – 您可以指定 Amazon Glacier 在完成任务后可以向其发布通知的 Amazon Simple Notification Service（Amazon SNS）主题。Amazon Glacier 只有在完成任务后才会发送通知。

  启动任务时，您可以为该任务指定 Amazon SNS 主题。除了在您的任务请求中指定 Amazon SNS 主题以外，如果您的文件库已为档案检索事件设置了通知配置，Amazon Glacier 也会向该 SNS 主题发布通知。有关更多信息，请参阅[在 Amazon Glacier 中配置文件库通知](configuring-notifications.md)。
+ **显式请求任务信息** – 您也可以使用 Amazon Glacier `Describe Job` API 操作（[描述任务（GET JobID）](api-describe-job-get.md)），以定期轮询任务信息。但是，建议使用 Amazon SNS 通知。

**注意**  
使用 Amazon SNS 通知获取的信息与调用 `Describe Job` API 操作所获取的信息相同。

## 档案检索选项


在启动检索档案的任务时，您可以根据访问时间和成本需求指定以下检索选项之一。有关检索定价的信息，请参阅 [Amazon Glacier 定价](https://www.amazonaws.cn/s3/glacier/pricing/)。
+ **加速** - 加速检索允许您在偶尔需要紧急请求还原档案时快速访问存储在 S3 Glacier Flexible Retrieval 存储类别或 S3 Intelligent-Tiering 归档访问层中的数据。对于除了最大型档案（250 MB 以上）之外的所有其他档案，使用加速检索访问的数据通常在 1 到 5 分钟内可用。预配置容量确保在您需要时，可以使用针对加速检索的检索容量。有关更多信息，请参阅[预配置容量](#api-downloading-an-archive-two-steps-retrieval-expedited-capacity)。
+ **标准** –标准检索允许您在数小时内访问您的任意档案。标准检索通常在 3 到 5 小时内完成。“标准”是未指定检索选项的检索请求的原定设置选项。
+ **批量** – 批量检索是 Amazon Glacier 最低成本的检索选项，使您可以在一天内以较低的成本检索大量（甚至是 PB 级）的数据。批量检索通常在 5 到 12 小时内完成。

下表总结了归档检索选项。有关定价的信息，请参阅 [Amazon Glacier 定价](https://www.amazonaws.cn/s3/glacier/pricing/)。


| 服务 | 加速 | 标准 | 批量 | 
| --- | --- | --- | --- | 
|  Amazon Glacier  |  1–5 分钟  |  3–5 小时  |  5–12 小时  | 

要进行`Expedited``Standard`、或`Bulk`检索，`Tier`请将 [https://docs.amazonaws.cn/AmazonS3/latest/API/RESTObjectPOSTrestore.html](https://docs.amazonaws.cn/AmazonS3/latest/API/RESTObjectPOSTrestore.html)REST API 操作请求中的请求元素设置为所需的选项，或 Amazon Command Line Interface (Amazon CLI) 或中的等效选项 Amazon SDKs。如果您购买了预配置容量，则所有加速检索都会通过您的预配置容量自动获得处理。

### 预配置容量


预配置容量帮助确保在您需要时，可以使用针对加速检索的检索容量。每个容量单位允许每 5 分钟至少执行三次加急检索，并提供高达每秒 150 兆字节的检索吞吐量 () MBps。

如果您的工作负载需要极高的稳定性和对数据子集可预测的访问性能（以分钟为单位），建议您购买预调配检索容量。没有预配置容量的加速检索通常也可以接受，但是在极少情况下会出现不寻常的高需求。不过，如果您需要随时可以访问加速检索，您必须购买预配置检索容量。

#### 购买预配置容量


您可以使用 Amazon Glacier 控制台、[购买预配置容量（POST provisioned-capacity）](api-PurchaseProvisionedCapacity.md) REST API 操作或购买预配置容量单位。 Amazon SDKs Amazon CLI有关预配置容量的定价信息，请参阅 [Amazon Glacier 定价](https://www.amazonaws.cn/s3/glacier/pricing/)。

预配置容量单位将持续一个月，从购买日期和时间开始计算。

如果开始日期为一个月的第 31 天，过期日期为下个月的最后一天。例如，如果开始日期为 8 月 31 日，则过期日期为 9 月 30 日。如果开始日期为 1 月 31 日，则过期日期为 2 月 28 日。

**使用 Amazon Glacier 控制台购买预配置容量**

1.  登录 Amazon Web Services 管理控制台 并在家中打开 Amazon Glacier [https://console.aws.amazon.com/glacier/主](https://console.amazonaws.cn/glacier/home)机。

1. 在左侧的导航窗格中，选择**数据检索设置**。

1. 在**预配置容量单位 (PCUs)** 下，选择**购买 PC** U。此时将显示**购买 PCU** 对话框。

1. 如果要购买预配置容量，请在**确认购买**框中输入 **confirm**。

1.  选择**购买 PCU**。

## 关于限范围的档案检索


当您从 Amazon Glacier 检索归档时，您可以选择性地指定要检索的归档范围（部分）。默认为检索整个档案。如果您要执行以下操作，指定字节范围会很有用：
+ **管理您的数据下载** – Amazon Glacier 允许您在检索请求完成后的 24 小时内下载检索的数据。因此，您可能只想要检索档案的某些部分，以便在给定的下载时间窗内管理下载时间表。
+ **检索大型档案的目标段** – 例如，假设您之前聚合了许多文件并以单一档案的形式上传了这些文件，您现在想检索这些文件中的一些文件。在这种情况下，您可以通过使用一个检索请求指定档案的范围，该范围包含您感兴趣的文件。或者，您可以启动多个检索请求，每个请求均具有一个针对一个或多个文件的范围。

当使用范围检索启动检索任务时，您必须提供以兆字节对齐的范围。也就是说，字节范围可以从零（档案的开头）开始，或者从其后的任何 1-MB 间隔（1 MB、2 MB、3 MB，依此类推）处开始。

该范围的结尾可以是您档案的结尾或大于范围开头的任何 1 MB 间隔处。此外，如果您要在（检索任务完成后）下载数据时获取校验和值，则您在任务启动中请求的范围还必须以树形哈希对齐。可以使用校验和来确保数据在传输过程中没有损坏。有关兆字节对齐和树形哈希对齐的更多信息，请参阅[下载数据时接收校验和](checksum-calculations-range.md)。

# 使用 Amazon Glacier 下载档案 适用于 Java 的 Amazon SDK
使用 Java 下载档案

适用于 Java 的 Amazon SDK APIs 提供的[高级版本和低级](using-aws-sdk.md)版本都提供了一种下载档案的方法。

**Topics**
+ [

## 使用的高级别 API 下载档案 适用于 Java 的 Amazon SDK
](#downloading-an-archive-using-java-highlevel-api)
+ [

## 使用的低级 API 下载档案 适用于 Java 的 Amazon SDK
](#downloading-an-archive-using-java-lowlevel-api)

## 使用的高级别 API 下载档案 适用于 Java 的 Amazon SDK


该高级 API 的 `ArchiveTransferManager` 类提供了您可以用来下载档案的 `download` 方法。

**重要**  
`ArchiveTransferManager` 类将创建 Amazon Simple Notification Service（Amazon SNS）主题，以及该主题订阅的 Amazon Simple Queue Service（Amazon SQS）队列。然后，它启动了档案检索任务，并对队列进行轮询以便找到可用档案。如果存档可用，则开始下载。有关检索时间的详细信息，请参阅[档案检索选项](downloading-an-archive-two-steps.md#api-downloading-an-archive-two-steps-retrieval-options)。

### 示例：使用的高级别 API 下载档案 适用于 Java 的 Amazon SDK


以下 Java 代码示例将从美国西部（俄勒冈州）区域（`us-west-2`）中的文件库（`examplevault`）中下载档案。

有关运行此示例的 step-by-step说明，请参阅[使用 Eclipse 运行 Amazon Glacier 的 Java 示例](using-aws-sdk-for-java.md#setting-up-and-testing-sdk-java)。您需要更新现有档案 ID 和已下载档案的本地文件保存路径旁显示的代码

**Example**  

```
import java.io.File;
import java.io.IOException;

import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.glacier.AmazonGlacierClient;
import com.amazonaws.services.glacier.transfer.ArchiveTransferManager;
import com.amazonaws.services.sns.AmazonSNSClient;
import com.amazonaws.services.sqs.AmazonSQSClient;


public class ArchiveDownloadHighLevel {
    public static String vaultName = "examplevault";
    public static String archiveId = "*** provide archive ID ***";
    public static String downloadFilePath  = "*** provide location to download archive ***";
    
    public static AmazonGlacierClient glacierClient;
    public static AmazonSQSClient sqsClient;
    public static AmazonSNSClient snsClient;
    
    public static void main(String[] args) throws IOException {
        
    	ProfileCredentialsProvider credentials = new ProfileCredentialsProvider();
        
        glacierClient = new AmazonGlacierClient(credentials);
        
        sqsClient = new AmazonSQSClient(credentials);
        snsClient = new AmazonSNSClient(credentials);
        glacierClient.setEndpoint("glacier.us-west-2.amazonaws.com");
        sqsClient.setEndpoint("sqs.us-west-2.amazonaws.com");
        snsClient.setEndpoint("sns.us-west-2.amazonaws.com");

        try {
            ArchiveTransferManager atm = new ArchiveTransferManager(glacierClient, sqsClient, snsClient);
            
            atm.download(vaultName, archiveId, new File(downloadFilePath));
            System.out.println("Downloaded file to " + downloadFilePath);
            
        } catch (Exception e)
        {
            System.err.println(e);
        }
    }
}
```

## 使用的低级 API 下载档案 适用于 Java 的 Amazon SDK


以下是使用 适用于 Java 的 Amazon SDK 低级 API 检索文件库清单的步骤。

 

1. 创建 `AmazonGlacierClient` 类（客户端）的实例。

   您需要指定要从中下载档案的 Amazon 区域。您使用此客户端执行的所有操作都适用于该 Amazon 区域。

1. 通过执行 `archive-retrieval` 方法启动 `initiateJob` 任务。

   您可以通过创建一个 `InitiateJobRequest` 类的实例提供任务信息，例如，您要下载的档案的档案 ID，以及您希望 Amazon Glacier（Amazon Glacier）向其发布任务完成消息的可选 Amazon SNS 主题。作为响应，Amazon Glacier 返回任务 ID。该响应位于一个 `InitiateJobResult` 类的实例中。

    

   ```
   JobParameters jobParameters = new JobParameters()
       .withArchiveId("*** provide an archive id ***")
       .withDescription("archive retrieval")
       .withRetrievalByteRange("*** provide a retrieval range***") // optional
       .withType("archive-retrieval");
   
   InitiateJobResult initiateJobResult = client.initiateJob(new InitiateJobRequest()
       .withJobParameters(jobParameters)
       .withVaultName(vaultName));  
             
   String jobId = initiateJobResult.getJobId();
   ```

   您可以选择指定字节范围，以请求 Amazon Glacier 只准备档案的一部分。例如，您可以通过添加以下语句更新前面的请求，以请求 Amazon Glacier 只准备档案的 1 MB 到 2 MB 部分。

    

   ```
   int ONE_MEG = 1048576;
   String retrievalByteRange = String.format("%s-%s", ONE_MEG, 2*ONE_MEG -1);
   
   JobParameters jobParameters = new JobParameters()
       .withType("archive-retrieval")
       .withArchiveId(archiveId)
       .withRetrievalByteRange(retrievalByteRange) 
       .withSNSTopic(snsTopicARN);
   
   InitiateJobResult initiateJobResult = client.initiateJob(new InitiateJobRequest()
       .withJobParameters(jobParameters)
       .withVaultName(vaultName));  
             
   String jobId = initiateJobResult.getJobId();
   ```

    

1. 等待任务完成。

   您必须等到任务输出已作好供您下载的准备。如果您在文件库中设置了标识 Amazon Simple Notification Service（Amazon SNS）主题的通知配置，或者在启动任务时指定了 Amazon SNS 主题，则 Amazon Glacier 会在完成任务后向该主题发送消息。

   此外，您还可以通过调用 `describeJob` 方法轮询 Amazon Glacier 来确定任务完成状态。尽管如此，使用 Amazon SNS 主题进行通知才是推荐的方法。

1. 通过执行 `getJobOutput` 方法下载任务输出（档案数据）。

   您可以通过创建一个 `GetJobOutputRequest` 类的实例来提供请求信息，例如，任务 ID 和文件库名称。Amazon Glacier 返回的输出位于 `GetJobOutputResult` 对象中。

    

   ```
   GetJobOutputRequest jobOutputRequest = new GetJobOutputRequest()
           .withJobId("*** provide a job ID ***")
           .withVaultName("*** provide a vault name ****");
   GetJobOutputResult jobOutputResult = client.getJobOutput(jobOutputRequest);
   
   // jobOutputResult.getBody() // Provides the input stream.
   ```

   前面的代码段会下载整个任务输出。您可以选择性地只检索输出的一部分，或者通过在您的 `GetJobOutputRequest` 中指定字节范围以较小的区块下载整个输出。

    

   ```
   GetJobOutputRequest jobOutputRequest = new GetJobOutputRequest()
           .withJobId("*** provide a job ID ***")
           .withRange("bytes=0-1048575")   // Download only the first 1 MB of the output.
           .withVaultName("*** provide a vault name ****");
   ```

   在响应您的 `GetJobOutput` 调用时，Amazon Glacier 返回您下载的数据部分的校验和（如果满足特定条件）。有关更多信息，请参阅[下载数据时接收校验和](checksum-calculations-range.md)。

   要确认下载中没有错误，您随后可以在客户端计算校验和，并将它与 Amazon Glacier 在响应中发送的校验和相比较。

   对于指定了可选范围的档案取回任务，当你获得任务描述时，它会包括你正在检索的范围的校验和 () SHA256 TreeHash。您可以使用此值进一步确认您稍后下载的整个字节范围的准确性。例如，如果您启动任务以检索以树形哈希对齐的档案范围，然后以区块下载输出，使得您的每个 `GetJobOutput` 请求均返回一个校验和，则您可以在客户端计算您下载的每个部分的校验和，然后计算树形哈希。您可以将它与 Amazon Glacier 为响应您的描述任务请求而返回的校验和相比较，以确认您下载的整个字节范围与 Amazon Glacier 中存储的字节范围相同。

    有关有效示例，请参阅[示例 2：使用分块 适用于 Java 的 Amazon SDK下载输出的低级 API 检索档案](#downloading-an-archive-with-range-using-java-example)。

### 示例 1：使用低级 API 检索档案 适用于 Java 的 Amazon SDK


以下 Java 代码示例会从指定的文件库下载档案。任务完成后，该示例会在单一 `getJobOutput` 调用中下载整个输出。有关以分块下载输出的示例，请参阅[示例 2：使用分块 适用于 Java 的 Amazon SDK下载输出的低级 API 检索档案](#downloading-an-archive-with-range-using-java-example)。

该示例执行以下任务：

 
+ 创建 Amazon Simple Notification Service（Amazon SNS）主题。

  完成任务后，Amazon Glacier 会向此主题发送通知。
+ 创建 Amazon Simple Queue Service（Amazon SQS）队列。

  该示例会向该队列附加策略，以使 Amazon SNS 主题能够向该队列发布消息。
+ 启动任务以下载指定的档案。

  在任务请求中，指定了创建的 Amazon SNS 主题，以便 Amazon Glacier 可以在完成任务后向该主题发布通知。
+ 定期检查 Amazon SQS 队列是否有包含该任务 ID 的消息。

  如果有消息，则分析 JSON，并检查任务是否已成功完成。如果已成功完成，则下载档案。
+ 通过删除它创建的 Amazon SNS 主题和 Amazon SQS 队列清除相关数据。

 

```
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.ObjectMapper;

import com.amazonaws.AmazonClientException;
import com.amazonaws.auth.policy.Policy;
import com.amazonaws.auth.policy.Principal;
import com.amazonaws.auth.policy.Resource;
import com.amazonaws.auth.policy.Statement;
import com.amazonaws.auth.policy.Statement.Effect;
import com.amazonaws.auth.policy.actions.SQSActions;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.glacier.AmazonGlacierClient;
import com.amazonaws.services.glacier.model.GetJobOutputRequest;
import com.amazonaws.services.glacier.model.GetJobOutputResult;
import com.amazonaws.services.glacier.model.InitiateJobRequest;
import com.amazonaws.services.glacier.model.InitiateJobResult;
import com.amazonaws.services.glacier.model.JobParameters;
import com.amazonaws.services.sns.AmazonSNSClient;
import com.amazonaws.services.sns.model.CreateTopicRequest;
import com.amazonaws.services.sns.model.CreateTopicResult;
import com.amazonaws.services.sns.model.DeleteTopicRequest;
import com.amazonaws.services.sns.model.SubscribeRequest;
import com.amazonaws.services.sns.model.SubscribeResult;
import com.amazonaws.services.sns.model.UnsubscribeRequest;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.CreateQueueRequest;
import com.amazonaws.services.sqs.model.CreateQueueResult;
import com.amazonaws.services.sqs.model.DeleteQueueRequest;
import com.amazonaws.services.sqs.model.GetQueueAttributesRequest;
import com.amazonaws.services.sqs.model.GetQueueAttributesResult;
import com.amazonaws.services.sqs.model.Message;
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
import com.amazonaws.services.sqs.model.SetQueueAttributesRequest;


public class AmazonGlacierDownloadArchiveWithSQSPolling {
    
    public static String archiveId = "*** provide archive ID ****";
    public static String vaultName = "*** provide vault name ***";
    public static String snsTopicName = "*** provide topic name ***";
    public static String sqsQueueName = "*** provide queue name ***";
    public static String sqsQueueARN;
    public static String sqsQueueURL;
    public static String snsTopicARN;
    public static String snsSubscriptionARN;
    public static String fileName = "*** provide file name ***";
    public static String region = "*** region ***"; 
    public static long sleepTime = 600; 
    public static AmazonGlacierClient client;
    public static AmazonSQSClient sqsClient;
    public static AmazonSNSClient snsClient;
    
    public static void main(String[] args) throws IOException {
        
    	ProfileCredentialsProvider credentials = new ProfileCredentialsProvider();

        client = new AmazonGlacierClient(credentials);
        client.setEndpoint("https://glacier." + region + ".amazonaws.com");
        sqsClient = new AmazonSQSClient(credentials);
        sqsClient.setEndpoint("https://sqs." + region + ".amazonaws.com");
        snsClient = new AmazonSNSClient(credentials);
        snsClient.setEndpoint("https://sns." + region + ".amazonaws.com");
                
        try {
            setupSQS();
            
            setupSNS();

            String jobId = initiateJobRequest();
            System.out.println("Jobid = " + jobId);
            
            Boolean success = waitForJobToComplete(jobId, sqsQueueURL);
            if (!success) { throw new Exception("Job did not complete successfully."); }
            
            downloadJobOutput(jobId);
            
            cleanUp();
            
        } catch (Exception e) {
            System.err.println("Archive retrieval failed.");
            System.err.println(e);
        }   
    }

    private static void setupSQS() {
        CreateQueueRequest request = new CreateQueueRequest()
            .withQueueName(sqsQueueName);
        CreateQueueResult result = sqsClient.createQueue(request);  
        sqsQueueURL = result.getQueueUrl();
                
        GetQueueAttributesRequest qRequest = new GetQueueAttributesRequest()
            .withQueueUrl(sqsQueueURL)
            .withAttributeNames("QueueArn");
        
        GetQueueAttributesResult qResult = sqsClient.getQueueAttributes(qRequest);
        sqsQueueARN = qResult.getAttributes().get("QueueArn");
        
        Policy sqsPolicy = 
            new Policy().withStatements(
                    new Statement(Effect.Allow)
                    .withPrincipals(Principal.AllUsers)
                    .withActions(SQSActions.SendMessage)
                    .withResources(new Resource(sqsQueueARN)));
        Map<String, String> queueAttributes = new HashMap<String, String>();
        queueAttributes.put("Policy", sqsPolicy.toJson());
        sqsClient.setQueueAttributes(new SetQueueAttributesRequest(sqsQueueURL, queueAttributes)); 

    }
    private static void setupSNS() {
        CreateTopicRequest request = new CreateTopicRequest()
            .withName(snsTopicName);
        CreateTopicResult result = snsClient.createTopic(request);
        snsTopicARN = result.getTopicArn();

        SubscribeRequest request2 = new SubscribeRequest()
            .withTopicArn(snsTopicARN)
            .withEndpoint(sqsQueueARN)
            .withProtocol("sqs");
        SubscribeResult result2 = snsClient.subscribe(request2);
                
        snsSubscriptionARN = result2.getSubscriptionArn();
    }
    private static String initiateJobRequest() {
        
        JobParameters jobParameters = new JobParameters()
            .withType("archive-retrieval")
            .withArchiveId(archiveId)
            .withSNSTopic(snsTopicARN);
        
        InitiateJobRequest request = new InitiateJobRequest()
            .withVaultName(vaultName)
            .withJobParameters(jobParameters);
        
        InitiateJobResult response = client.initiateJob(request);
        
        return response.getJobId();
    }
    
    private static Boolean waitForJobToComplete(String jobId, String sqsQueueUrl) throws InterruptedException, JsonParseException, IOException {
        
        Boolean messageFound = false;
        Boolean jobSuccessful = false;
        ObjectMapper mapper = new ObjectMapper();
        JsonFactory factory = mapper.getJsonFactory();
        
        while (!messageFound) {
            List<Message> msgs = sqsClient.receiveMessage(
               new ReceiveMessageRequest(sqsQueueUrl).withMaxNumberOfMessages(10)).getMessages();

            if (msgs.size() > 0) {
                for (Message m : msgs) {
                    JsonParser jpMessage = factory.createJsonParser(m.getBody());
                    JsonNode jobMessageNode = mapper.readTree(jpMessage);
                    String jobMessage = jobMessageNode.get("Message").getTextValue();
                    
                    JsonParser jpDesc = factory.createJsonParser(jobMessage);
                    JsonNode jobDescNode = mapper.readTree(jpDesc);
                    String retrievedJobId = jobDescNode.get("JobId").getTextValue();
                    String statusCode = jobDescNode.get("StatusCode").getTextValue();
                    if (retrievedJobId.equals(jobId)) {
                        messageFound = true;
                        if (statusCode.equals("Succeeded")) {
                            jobSuccessful = true;
                        }
                    }
                }
                
            } else {
              Thread.sleep(sleepTime * 1000); 
            }
          }
        return (messageFound && jobSuccessful);
    }
    
    private static void downloadJobOutput(String jobId) throws IOException {
        
        GetJobOutputRequest getJobOutputRequest = new GetJobOutputRequest()
            .withVaultName(vaultName)
            .withJobId(jobId);
        GetJobOutputResult getJobOutputResult = client.getJobOutput(getJobOutputRequest);
    
        InputStream input = new BufferedInputStream(getJobOutputResult.getBody());
        OutputStream output = null;
        try {
            output = new BufferedOutputStream(new FileOutputStream(fileName));

            byte[] buffer = new byte[1024 * 1024];

            int bytesRead = 0;
            do {
                bytesRead = input.read(buffer);
                if (bytesRead <= 0) break;
                output.write(buffer, 0, bytesRead);
            } while (bytesRead > 0);
        } catch (IOException e) {
            throw new AmazonClientException("Unable to save archive", e);
        } finally {
            try {input.close();}  catch (Exception e) {}
            try {output.close();} catch (Exception e) {}
        }
        System.out.println("Retrieved archive to " + fileName);
    }
    
    private static void cleanUp() {
        snsClient.unsubscribe(new UnsubscribeRequest(snsSubscriptionARN));
        snsClient.deleteTopic(new DeleteTopicRequest(snsTopicARN));
        sqsClient.deleteQueue(new DeleteQueueRequest(sqsQueueURL));
    }
}
```

### 示例 2：使用分块 适用于 Java 的 Amazon SDK下载输出的低级 API 检索档案


以下 Java 代码示例从 Amazon Glacier 检索档案。该代码示例会通过在 `GetJobOutputRequest` 数据元中指定字节范围来以区块下载任务输出。

 

```
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.amazonaws.auth.policy.Policy;
import com.amazonaws.auth.policy.Principal;
import com.amazonaws.auth.policy.Resource;
import com.amazonaws.auth.policy.Statement;
import com.amazonaws.auth.policy.Statement.Effect;
import com.amazonaws.auth.policy.actions.SQSActions;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.glacier.AmazonGlacierClient;
import com.amazonaws.services.glacier.TreeHashGenerator;
import com.amazonaws.services.glacier.model.GetJobOutputRequest;
import com.amazonaws.services.glacier.model.GetJobOutputResult;
import com.amazonaws.services.glacier.model.InitiateJobRequest;
import com.amazonaws.services.glacier.model.InitiateJobResult;
import com.amazonaws.services.glacier.model.JobParameters;
import com.amazonaws.services.sns.AmazonSNSClient;
import com.amazonaws.services.sns.model.CreateTopicRequest;
import com.amazonaws.services.sns.model.CreateTopicResult;
import com.amazonaws.services.sns.model.DeleteTopicRequest;
import com.amazonaws.services.sns.model.SubscribeRequest;
import com.amazonaws.services.sns.model.SubscribeResult;
import com.amazonaws.services.sns.model.UnsubscribeRequest;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.CreateQueueRequest;
import com.amazonaws.services.sqs.model.CreateQueueResult;
import com.amazonaws.services.sqs.model.DeleteQueueRequest;
import com.amazonaws.services.sqs.model.GetQueueAttributesRequest;
import com.amazonaws.services.sqs.model.GetQueueAttributesResult;
import com.amazonaws.services.sqs.model.Message;
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
import com.amazonaws.services.sqs.model.SetQueueAttributesRequest;


public class ArchiveDownloadLowLevelWithRange {
    
    public static String vaultName = "*** provide vault name ***";
    public static String archiveId = "*** provide archive id ***";
    public static String snsTopicName = "glacier-temp-sns-topic";
    public static String sqsQueueName = "glacier-temp-sqs-queue";
    public static long downloadChunkSize = 4194304; // 4 MB  
    public static String sqsQueueARN;
    public static String sqsQueueURL;
    public static String snsTopicARN;
    public static String snsSubscriptionARN;
    public static String fileName = "*** provide file name to save archive to ***";
    public static String region   = "*** region ***";
    public static long sleepTime  = 600; 
    
    public static AmazonGlacierClient client;
    public static AmazonSQSClient sqsClient;
    public static AmazonSNSClient snsClient; 
    
    public static void main(String[] args) throws IOException {
        
    	ProfileCredentialsProvider credentials = new ProfileCredentialsProvider();

        client = new AmazonGlacierClient(credentials);
        client.setEndpoint("https://glacier." + region + ".amazonaws.com");
        sqsClient = new AmazonSQSClient(credentials);
        sqsClient.setEndpoint("https://sqs." + region + ".amazonaws.com");
        snsClient = new AmazonSNSClient(credentials);
        snsClient.setEndpoint("https://sns." + region + ".amazonaws.com");
        
        try {
            setupSQS();
            
            setupSNS();

            String jobId = initiateJobRequest();
            System.out.println("Jobid = " + jobId);
            
            long archiveSizeInBytes = waitForJobToComplete(jobId, sqsQueueURL);
            if (archiveSizeInBytes==-1) { throw new Exception("Job did not complete successfully."); }
            
            downloadJobOutput(jobId, archiveSizeInBytes);
            
            cleanUp();
            
        } catch (Exception e) {
            System.err.println("Archive retrieval failed.");
            System.err.println(e);
        }   
    }

    private static void setupSQS() {
        CreateQueueRequest request = new CreateQueueRequest()
            .withQueueName(sqsQueueName);
        CreateQueueResult result = sqsClient.createQueue(request);  
        sqsQueueURL = result.getQueueUrl();
                
        GetQueueAttributesRequest qRequest = new GetQueueAttributesRequest()
            .withQueueUrl(sqsQueueURL)
            .withAttributeNames("QueueArn");
        
        GetQueueAttributesResult qResult = sqsClient.getQueueAttributes(qRequest);
        sqsQueueARN = qResult.getAttributes().get("QueueArn");
        
        Policy sqsPolicy = 
            new Policy().withStatements(
                    new Statement(Effect.Allow)
                    .withPrincipals(Principal.AllUsers)
                    .withActions(SQSActions.SendMessage)
                    .withResources(new Resource(sqsQueueARN)));
        Map<String, String> queueAttributes = new HashMap<String, String>();
        queueAttributes.put("Policy", sqsPolicy.toJson());
        sqsClient.setQueueAttributes(new SetQueueAttributesRequest(sqsQueueURL, queueAttributes)); 

    }
    private static void setupSNS() {
        CreateTopicRequest request = new CreateTopicRequest()
            .withName(snsTopicName);
        CreateTopicResult result = snsClient.createTopic(request);
        snsTopicARN = result.getTopicArn();

        SubscribeRequest request2 = new SubscribeRequest()
            .withTopicArn(snsTopicARN)
            .withEndpoint(sqsQueueARN)
            .withProtocol("sqs");
        SubscribeResult result2 = snsClient.subscribe(request2);
                
        snsSubscriptionARN = result2.getSubscriptionArn();
    }
    private static String initiateJobRequest() {
        
        JobParameters jobParameters = new JobParameters()
            .withType("archive-retrieval")
            .withArchiveId(archiveId)
            .withSNSTopic(snsTopicARN);
        
        InitiateJobRequest request = new InitiateJobRequest()
            .withVaultName(vaultName)
            .withJobParameters(jobParameters);
        
        InitiateJobResult response = client.initiateJob(request);
        
        return response.getJobId();
    }
    
    private static long waitForJobToComplete(String jobId, String sqsQueueUrl) throws InterruptedException, JsonParseException, IOException {
        
        Boolean messageFound = false;
        Boolean jobSuccessful = false;
        long archiveSizeInBytes = -1;
        ObjectMapper mapper = new ObjectMapper();
        JsonFactory factory = mapper.getFactory();
        
        while (!messageFound) {
            List<Message> msgs = sqsClient.receiveMessage(
               new ReceiveMessageRequest(sqsQueueUrl).withMaxNumberOfMessages(10)).getMessages();

            if (msgs.size() > 0) {
                for (Message m : msgs) {
                    JsonParser jpMessage = factory.createJsonParser(m.getBody());
                    JsonNode jobMessageNode = mapper.readTree(jpMessage);
                    String jobMessage = jobMessageNode.get("Message").textValue();
                    
                    JsonParser jpDesc = factory.createJsonParser(jobMessage);
                    JsonNode jobDescNode = mapper.readTree(jpDesc);
                    String retrievedJobId = jobDescNode.get("JobId").textValue();
                    String statusCode = jobDescNode.get("StatusCode").textValue();
                    archiveSizeInBytes = jobDescNode.get("ArchiveSizeInBytes").longValue();
                    if (retrievedJobId.equals(jobId)) {
                        messageFound = true;
                        if (statusCode.equals("Succeeded")) {
                            jobSuccessful = true;
                        }
                    }
                }
                
            } else {
              Thread.sleep(sleepTime * 1000); 
            }
          }
        return (messageFound && jobSuccessful) ? archiveSizeInBytes : -1;
    }
    
    private static void downloadJobOutput(String jobId, long archiveSizeInBytes) throws IOException {
        
        if (archiveSizeInBytes < 0) {
            System.err.println("Nothing to download.");
            return;
        }

        System.out.println("archiveSizeInBytes: " + archiveSizeInBytes);
        FileOutputStream fstream = new FileOutputStream(fileName);
        long startRange = 0;
        long endRange = (downloadChunkSize > archiveSizeInBytes) ? archiveSizeInBytes -1 : downloadChunkSize - 1;

        do {

            GetJobOutputRequest getJobOutputRequest = new GetJobOutputRequest()
                .withVaultName(vaultName)
                .withRange("bytes=" + startRange + "-" + endRange)
                .withJobId(jobId);
            GetJobOutputResult getJobOutputResult = client.getJobOutput(getJobOutputRequest);

            BufferedInputStream is = new BufferedInputStream(getJobOutputResult.getBody());     
            byte[] buffer = new byte[(int)(endRange - startRange + 1)];

            System.out.println("Checksum received: " + getJobOutputResult.getChecksum());
            System.out.println("Content range " + getJobOutputResult.getContentRange());

            
            int totalRead = 0;
            while (totalRead < buffer.length) {
                int bytesRemaining = buffer.length - totalRead;
                int read = is.read(buffer, totalRead, bytesRemaining);
                if (read > 0) {
                    totalRead = totalRead + read;                             
                } else {
                    break;
                }
                
            }
            System.out.println("Calculated checksum: " + TreeHashGenerator.calculateTreeHash(new ByteArrayInputStream(buffer)));
            System.out.println("read = " + totalRead);
            fstream.write(buffer);
            
            startRange = startRange + (long)totalRead;
            endRange = ((endRange + downloadChunkSize) >  archiveSizeInBytes) ? archiveSizeInBytes : (endRange + downloadChunkSize); 
            is.close();
        } while (endRange <= archiveSizeInBytes  && startRange < archiveSizeInBytes);
        
        fstream.close();
        System.out.println("Retrieved file to " + fileName);

    }
    
    private static void cleanUp() {
        snsClient.unsubscribe(new UnsubscribeRequest(snsSubscriptionARN));
        snsClient.deleteTopic(new DeleteTopicRequest(snsTopicARN));
        sqsClient.deleteQueue(new DeleteQueueRequest(sqsQueueURL));
    }
}
```

# 使用 Amazon Glacier 下载档案 适用于 .NET 的 Amazon SDK
使用 .NET 下载档案

适用于.NET 的 Amazon SDK APIs 提供的[高级版本和低级](using-aws-sdk.md)版本都提供了一种下载档案的方法。

**Topics**
+ [

## 使用的高级别 API 下载档案 适用于 .NET 的 Amazon SDK
](#downloading-an-archive-using-dotnet-highlevel-api)
+ [

## 使用的低级 API 下载档案 适用于 .NET 的 Amazon SDK
](#downloading-an-archive-using-dotnet-lowlevel-api)

## 使用的高级别 API 下载档案 适用于 .NET 的 Amazon SDK


该高级 API 的 `ArchiveTransferManager` 类提供了您可以用来下载档案的 `Download` 方法。

**重要**  
`ArchiveTransferManager` 类将创建 Amazon Simple Notification Service（Amazon SNS）主题，以及该主题订阅的 Amazon Simple Queue Service（Amazon SQS）队列。然后，它启动了档案检索任务，并对队列进行轮询以便找到可用档案。如果存档可用，则开始下载。有关检索时间的详细信息，请参阅[档案检索选项](downloading-an-archive-two-steps.md#api-downloading-an-archive-two-steps-retrieval-options)

### 示例：使用的高级别 API 下载档案 适用于 .NET 的 Amazon SDK


以下 C\$1 代码示例将从美国西部（俄勒冈州）区域中的文件库（`examplevault`） 中下载档案。

有关如何运行此示例的 step-by-step说明，请参阅[运行代码示例](using-aws-sdk-for-dot-net.md#setting-up-and-testing-sdk-dotnet)。您需要更新现有档案 ID 和已下载档案的本地文件保存路径旁显示的代码

```
using System;
using Amazon.Glacier;
using Amazon.Glacier.Transfer;
using Amazon.Runtime;

namespace glacier.amazon.com.docsamples
{
  class ArchiveDownloadHighLevel
  {
    static string vaultName        = "examplevault";
    static string archiveId        = "*** Provide archive ID ***";
    static string downloadFilePath = "*** Provide the file name and path to where to store the download ***";

    public static void Main(string[] args)
    {
      try
      {
        var manager = new ArchiveTransferManager(Amazon.RegionEndpoint.USWest2);

        var options = new DownloadOptions();
        options.StreamTransferProgress += ArchiveDownloadHighLevel.progress;
        // Download an archive.
        Console.WriteLine("Intiating the archive retrieval job and then polling SQS queue for the archive to be available.");
        Console.WriteLine("Once the archive is available, downloading will begin.");
        manager.Download(vaultName, archiveId, downloadFilePath, options);
        Console.WriteLine("To continue, press Enter");
        Console.ReadKey();
      }
      catch (AmazonGlacierException e) { Console.WriteLine(e.Message); }
      catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
      catch (Exception e) { Console.WriteLine(e.Message); }
      Console.WriteLine("To continue, press Enter");
      Console.ReadKey();
    }

    static int currentPercentage = -1;
    static void progress(object sender, StreamTransferProgressArgs args)
    {
      if (args.PercentDone != currentPercentage)
      {
        currentPercentage = args.PercentDone;
        Console.WriteLine("Downloaded {0}%", args.PercentDone);
      }
    }
  }
}
```

## 使用的低级 API 下载档案 适用于 .NET 的 Amazon SDK


以下是使用 适用于 .NET 的 Amazon SDK低级 API 下载 Amazon Glacier（Amazon Glacier）档案的步骤。

1. 创建 `AmazonGlacierClient` 类（客户端）的实例。

   您需要指定要从中下载档案的 Amazon 区域。您使用此客户端执行的所有操作都适用于该 Amazon 区域。

1. 通过执行 `archive-retrieval` 方法启动 `InitiateJob` 任务。

   您可以通过创建一个 `InitiateJobRequest` 类的实例提供任务信息，例如，您要下载的档案的档案 ID，以及您希望 Amazon Glacier 向其发布任务完成消息的可选 Amazon SNS 主题。作为响应，Amazon Glacier 返回任务 ID。该响应位于一个 `InitiateJobResponse` 类的实例中。

   ```
   AmazonGlacierClient client;
   client = new AmazonGlacierClient(Amazon.RegionEndpoint.USWest2);
   
   InitiateJobRequest initJobRequest = new InitiateJobRequest()
   {
     VaultName = vaultName,
     JobParameters = new JobParameters()
     {
       Type      = "archive-retrieval",
       ArchiveId = "*** Provide archive id ***",
       SNSTopic  = "*** Provide Amazon SNS topic ARN ***",
     }
   };
   
   InitiateJobResponse initJobResponse = client.InitiateJob(initJobRequest);
   string jobId = initJobResponse.JobId;
   ```

   您可以选择指定字节范围，以请求 Amazon Glacier 只准备档案的一部分，如以下请求所示。该请求指定 Amazon Glacier 只准备档案的 1 MB 到 2 MB 部分。

   ```
   AmazonGlacierClient client;
   client = new AmazonGlacierClient(Amazon.RegionEndpoint.USWest2);
   
   
   InitiateJobRequest initJobRequest = new InitiateJobRequest()
   {
     VaultName = vaultName,
     JobParameters = new JobParameters()
     {
       Type      = "archive-retrieval",
       ArchiveId = "*** Provide archive id ***",
       SNSTopic  = "*** Provide Amazon SNS topic ARN ***",
     }
   };
   // Specify byte range.
   int ONE_MEG = 1048576;
   initJobRequest.JobParameters.RetrievalByteRange = string.Format("{0}-{1}", ONE_MEG, 2 * ONE_MEG -1);
   
   InitiateJobResponse initJobResponse = client.InitiateJob(initJobRequest);
   string jobId = initJobResponse.JobId;
   ```

1. 等待任务完成。

   您必须等到任务输出已作好供您下载的准备。如果您在文件库中设置了标识 Amazon Simple Notification Service（Amazon SNS）主题的通知配置，或者在启动任务时指定了 Amazon SNS 主题，则 Amazon Glacier 会在完成任务后向该主题发送消息。以下部分给出的代码示例使用适用于 Amazon Glacier 的 Amazon SNS 来发布消息。

   此外，您还可以通过调用 `DescribeJob` 方法轮询 Amazon Glacier 来确定任务完成状态。尽管如此，使用 Amazon SNS 主题进行通知才是推荐的方法。

1. 通过执行 `GetJobOutput` 方法下载任务输出（档案数据）。

   您可以通过创建一个 `GetJobOutputRequest` 类的实例来提供请求信息，例如，任务 ID 和文件库名称。Amazon Glacier 返回的输出位于 `GetJobOutputResponse` 对象中。

   ```
   GetJobOutputRequest getJobOutputRequest = new GetJobOutputRequest()
   {
     JobId = jobId,
     VaultName = vaultName
   };
   
   GetJobOutputResponse getJobOutputResponse = client.GetJobOutput(getJobOutputRequest);
   using (Stream webStream = getJobOutputResponse.Body)
   {
     using (Stream fileToSave = File.OpenWrite(fileName))
     {
        CopyStream(webStream, fileToSave);
     }
   }
   ```

   前面的代码段会下载整个任务输出。您可以选择性地只检索输出的一部分，或者通过在您的 `GetJobOutputRequest` 中指定字节范围以较小的区块下载整个输出。

   ```
   GetJobOutputRequest getJobOutputRequest = new GetJobOutputRequest()
   {
     JobId = jobId,
     VaultName = vaultName
   };
   getJobOutputRequest.SetRange(0, 1048575); // Download only the first 1 MB chunk of the output.
   ```

   在响应您的 `GetJobOutput` 调用时，Amazon Glacier 返回您下载的数据部分的校验和（如果满足特定条件）。有关更多信息，请参阅[下载数据时接收校验和](checksum-calculations-range.md)。

   要确认下载中没有错误，您随后可以在客户端计算校验和，并将它与 Amazon Glacier 在响应中发送的校验和相比较。

   对于指定了可选范围的档案取回作业，当你获得任务描述时，它会包括你要检索的范围的校验和 (SHA256TreeHash)。你可以使用这个值来进一步验证你以后下载的整个字节范围的准确性。例如，如果您启动任务以检索以树形哈希对齐的档案范围，然后以区块下载输出，使得您的每个 `GetJobOutput` 请求均返回一个校验和，则您可以在客户端计算您下载的每个部分的校验和，然后计算树形哈希。您可以将它与 Amazon Glacier 为响应您的描述任务请求而返回的校验和相比较，以确认您下载的整个字节范围与 Amazon Glacier 中存储的字节范围相同。

   

   有关有效示例，请参阅[示例 2：使用分块 适用于 .NET 的 Amazon SDK下载输出的低级 API 检索档案](#creating-vaults-sdk-dotnet-example2)。

### 示例 1：使用低级 API 检索档案 适用于 .NET 的 Amazon SDK


以下 C\$1 代码示例会从指定的文件库下载档案。任务完成后，该示例会在单一 `GetJobOutput` 调用中下载整个输出。有关以分块下载输出的示例，请参阅[示例 2：使用分块 适用于 .NET 的 Amazon SDK下载输出的低级 API 检索档案](#creating-vaults-sdk-dotnet-example2)。

该示例执行以下任务：
+ 设置 Amazon Simple Notification Service（Amazon SNS）主题 

  完成任务后，Amazon Glacier 会向此主题发送通知。
+ 设置 Amazon Simple Queue Service（Amazon SQS）队列 

  该示例会向该队列附加策略，以使 Amazon SNS 主题能够发布消息。
+ 启动任务以下载指定的档案。

  在任务请求中，该示例会指定 Amazon SNS 主题，以便 Amazon Glacier 可以在完成任务后发送消息。
+ 定期检查 Amazon SQS 队列是否有消息。

  如果有消息，则分析 JSON，并检查任务是否已成功完成。如果已成功完成，则下载档案。该代码示例使用 JSON.NET 库（请参阅 [JSON.NET](http://json.codeplex.com/)）来分析 JSON。
+ 通过删除它创建的 Amazon SNS 主题和 Amazon SQS 队列清除相关数据。

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Amazon.Glacier;
using Amazon.Glacier.Model;
using Amazon.Runtime;
using Amazon.SimpleNotificationService;
using Amazon.SimpleNotificationService.Model;
using Amazon.SQS;
using Amazon.SQS.Model;
using Newtonsoft.Json;

namespace glacier.amazon.com.docsamples
{
  class ArchiveDownloadLowLevelUsingSNSSQS
  {
    static string topicArn;
    static string queueUrl;
    static string queueArn;
    static string vaultName = "*** Provide vault name ***";
    static string archiveID = "*** Provide archive ID ***";
    static string fileName  = "*** Provide the file name and path to where to store downloaded archive ***";
    static AmazonSimpleNotificationServiceClient snsClient;
    static AmazonSQSClient sqsClient;
    const string SQS_POLICY =
        "{" +
        "    \"Version\" : \"2012-10-17\",&TCX5-2025-waiver;" +
        "    \"Statement\" : [" +
        "        {" +
        "            \"Sid\" : \"sns-rule\"," +
        "            \"Effect\" : \"Allow\"," +
        "            \"Principal\" : {\"Service\" : \"sns.amazonaws.com\" }," +
        "            \"Action\"    : \"sqs:SendMessage\"," +
        "            \"Resource\"  : \"{QueueArn}\"," +
        "            \"Condition\" : {" +
        "                \"ArnLike\" : {" +
        "                    \"aws:SourceArn\" : \"{TopicArn}\"" +
        "                }" +
        "            }" +
        "        }" +
        "    ]" +
        "}";

    public static void Main(string[] args)
    {
      AmazonGlacierClient client;
      try
      {
        using (client = new AmazonGlacierClient(Amazon.RegionEndpoint.USWest2))
        {
          Console.WriteLine("Setup SNS topic and SQS queue."); 
          SetupTopicAndQueue();
          Console.WriteLine("To continue, press Enter"); Console.ReadKey();
          Console.WriteLine("Retrieving...");
          RetrieveArchive(client);
        }
        Console.WriteLine("Operations successful. To continue, press Enter");
        Console.ReadKey();
      }
      catch (AmazonGlacierException e) { Console.WriteLine(e.Message); }
      catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
      catch (Exception e) { Console.WriteLine(e.Message); }
      finally
      {
        // Delete SNS topic and SQS queue.
        snsClient.DeleteTopic(new DeleteTopicRequest() { TopicArn = topicArn });
        sqsClient.DeleteQueue(new DeleteQueueRequest() { QueueUrl = queueUrl });
      }
    }

    static void SetupTopicAndQueue()
    {
      snsClient = new AmazonSimpleNotificationServiceClient(Amazon.RegionEndpoint.USWest2);
      sqsClient = new AmazonSQSClient(Amazon.RegionEndpoint.USWest2);

      long ticks = DateTime.Now.Ticks;
      topicArn = snsClient.CreateTopic(new CreateTopicRequest { Name = "GlacierDownload-" + ticks }).TopicArn;
      Console.Write("topicArn: "); Console.WriteLine(topicArn);

      CreateQueueRequest createQueueRequest = new CreateQueueRequest();
      createQueueRequest.QueueName = "GlacierDownload-" + ticks;
      CreateQueueResponse createQueueResponse = sqsClient.CreateQueue(createQueueRequest);
      queueUrl = createQueueResponse.QueueUrl;
      Console.Write("QueueURL: "); Console.WriteLine(queueUrl);

      GetQueueAttributesRequest getQueueAttributesRequest = new GetQueueAttributesRequest();
      getQueueAttributesRequest.AttributeNames = new List<string> { "QueueArn" };
      getQueueAttributesRequest.QueueUrl = queueUrl;
      GetQueueAttributesResponse response = sqsClient.GetQueueAttributes(getQueueAttributesRequest);
      queueArn = response.QueueARN;
      Console.Write("QueueArn: "); Console.WriteLine(queueArn);

      // Setup the Amazon SNS topic to publish to the SQS queue.
      snsClient.Subscribe(new SubscribeRequest()
      {
        Protocol = "sqs",
        Endpoint = queueArn,
        TopicArn = topicArn
      });

      // Add policy to the queue so SNS can send messages to the queue.
      var policy = SQS_POLICY.Replace("{TopicArn}", topicArn).Replace("{QueueArn}", queueArn);

      sqsClient.SetQueueAttributes(new SetQueueAttributesRequest()
      {
          QueueUrl = queueUrl,
          Attributes = new Dictionary<string, string>
          {
              { QueueAttributeName.Policy, policy }
          }
      });
    }

    static void RetrieveArchive(AmazonGlacierClient client)
    {
      // Initiate job.
      InitiateJobRequest initJobRequest = new InitiateJobRequest()
      {
        VaultName = vaultName,
        JobParameters = new JobParameters()
        {
          Type = "archive-retrieval", 
          ArchiveId = archiveID,
          Description = "This job is to download archive.",
          SNSTopic = topicArn,
        }
      };
      InitiateJobResponse initJobResponse = client.InitiateJob(initJobRequest);
      string jobId = initJobResponse.JobId;

      // Check queue for a message and if job completed successfully, download archive.
      ProcessQueue(jobId, client);
    }

    private static void ProcessQueue(string jobId, AmazonGlacierClient client)
    {
      ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest() { QueueUrl = queueUrl, MaxNumberOfMessages = 1 }; 
      bool jobDone = false;
      while (!jobDone)
      {
        Console.WriteLine("Poll SQS queue");
        ReceiveMessageResponse receiveMessageResponse = sqsClient.ReceiveMessage(receiveMessageRequest); 
        if (receiveMessageResponse.Messages.Count == 0)
        {
          Thread.Sleep(10000 * 60);
          continue;
        }
        Console.WriteLine("Got message");
        Message message = receiveMessageResponse.Messages[0];
        Dictionary<string, string> outerLayer = JsonConvert.DeserializeObject<Dictionary<string, string>>(message.Body);
        Dictionary<string, object> fields = JsonConvert.DeserializeObject<Dictionary<string, object>>(outerLayer["Message"]);
        string statusCode = fields["StatusCode"] as string;

        if (string.Equals(statusCode, GlacierUtils.JOB_STATUS_SUCCEEDED, StringComparison.InvariantCultureIgnoreCase))
        {
          Console.WriteLine("Downloading job output");
          DownloadOutput(jobId, client); // Save job output to the specified file location.
        }
        else if (string.Equals(statusCode, GlacierUtils.JOB_STATUS_FAILED, StringComparison.InvariantCultureIgnoreCase))
          Console.WriteLine("Job failed... cannot download the archive.");

        jobDone = true;
        sqsClient.DeleteMessage(new DeleteMessageRequest() { QueueUrl = queueUrl, ReceiptHandle = message.ReceiptHandle });
      }
    }

    private static void DownloadOutput(string jobId, AmazonGlacierClient client)
    {
      GetJobOutputRequest getJobOutputRequest = new GetJobOutputRequest()
      {
        JobId = jobId,
        VaultName = vaultName
      };
      
      GetJobOutputResponse getJobOutputResponse = client.GetJobOutput(getJobOutputRequest);
      using (Stream webStream = getJobOutputResponse.Body)
      {
          using (Stream fileToSave = File.OpenWrite(fileName))
          {
              CopyStream(webStream, fileToSave);
          }
      }
    }

    public static void CopyStream(Stream input, Stream output)
    {
      byte[] buffer = new byte[65536];
      int length;
      while ((length = input.Read(buffer, 0, buffer.Length)) > 0)
      {
        output.Write(buffer, 0, length);
      }
    }
  }
}
```

### 示例 2：使用分块 适用于 .NET 的 Amazon SDK下载输出的低级 API 检索档案


以下 C\$1 代码示例从 Amazon Glacier 检索档案。该代码示例会通过在 `GetJobOutputRequest` 数据元中指定字节范围来以区块下载任务输出。

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Amazon.Glacier;
using Amazon.Glacier.Model;
using Amazon.Glacier.Transfer;
using Amazon.Runtime;
using Amazon.SimpleNotificationService;
using Amazon.SimpleNotificationService.Model;
using Amazon.SQS;
using Amazon.SQS.Model;
using Newtonsoft.Json;
using System.Collections.Specialized;

namespace glacier.amazon.com.docsamples
{
  class ArchiveDownloadLowLevelUsingSQLSNSOutputUsingRange
  {
    static string topicArn;
    static string queueUrl;
    static string queueArn;
    static string vaultName = "*** Provide vault name ***";
    static string archiveId = "*** Provide archive ID ***";
    static string fileName  = "*** Provide the file name and path to where to store downloaded archive ***";
    static AmazonSimpleNotificationServiceClient snsClient;
    static AmazonSQSClient sqsClient;
    const string SQS_POLICY =
        "{" +
        "    \"Version\" : \"2012-10-17\",&TCX5-2025-waiver;" +
        "    \"Statement\" : [" +
        "        {" +
        "            \"Sid\" : \"sns-rule\"," +
        "            \"Effect\" : \"Allow\"," +
        "            \"Principal\" : {\"AWS\" : \"arn:aws:iam::123456789012:root\" }," +
        "            \"Action\"  : \"sqs:SendMessage\"," +
        "            \"Resource\"  : \"{QuernArn}\"," +
        "            \"Condition\" : {" +
        "                \"ArnLike\" : {" +
        "                    \"aws:SourceArn\" : \"{TopicArn}\"" +
        "                }" +
        "            }" +
        "        }" +
        "    ]" +
        "}";

    public static void Main(string[] args)
    {
      AmazonGlacierClient client;

      try
      {
          using (client = new AmazonGlacierClient(Amazon.RegionEndpoint.USWest2))
          {
              Console.WriteLine("Setup SNS topic and SQS queue.");
              SetupTopicAndQueue();
              Console.WriteLine("To continue, press Enter"); Console.ReadKey();

              Console.WriteLine("Download archive");
              DownloadAnArchive(archiveId, client);
        }
        Console.WriteLine("Operations successful. To continue, press Enter");
        Console.ReadKey();
      }
      catch (AmazonGlacierException e) { Console.WriteLine(e.Message); }
      catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
      catch (Exception e) { Console.WriteLine(e.Message); }
      finally
      {
        // Delete SNS topic and SQS queue.
        snsClient.DeleteTopic(new DeleteTopicRequest() { TopicArn = topicArn });
        sqsClient.DeleteQueue(new DeleteQueueRequest() { QueueUrl = queueUrl });
      }
    }

       static void SetupTopicAndQueue()
    {
      long ticks = DateTime.Now.Ticks;
      
      // Setup SNS topic.
      snsClient = new AmazonSimpleNotificationServiceClient(Amazon.RegionEndpoint.USWest2);
      sqsClient = new AmazonSQSClient(Amazon.RegionEndpoint.USWest2);

      topicArn = snsClient.CreateTopic(new CreateTopicRequest { Name = "GlacierDownload-" + ticks }).TopicArn;
      Console.Write("topicArn: "); Console.WriteLine(topicArn);

      CreateQueueRequest createQueueRequest = new CreateQueueRequest();
      createQueueRequest.QueueName = "GlacierDownload-" + ticks;
      CreateQueueResponse createQueueResponse = sqsClient.CreateQueue(createQueueRequest);
      queueUrl = createQueueResponse.QueueUrl;
      Console.Write("QueueURL: "); Console.WriteLine(queueUrl);

      GetQueueAttributesRequest getQueueAttributesRequest = new GetQueueAttributesRequest();
      getQueueAttributesRequest.AttributeNames = new List<string> { "QueueArn" };
      getQueueAttributesRequest.QueueUrl = queueUrl;
      GetQueueAttributesResponse response = sqsClient.GetQueueAttributes(getQueueAttributesRequest);
      queueArn = response.QueueARN;
      Console.Write("QueueArn: "); Console.WriteLine(queueArn);

      // Setup the Amazon SNS topic to publish to the SQS queue.
      snsClient.Subscribe(new SubscribeRequest()
      {
        Protocol = "sqs",
        Endpoint = queueArn,
        TopicArn = topicArn
      });

      // Add the policy to the queue so SNS can send messages to the queue.
      var policy = SQS_POLICY.Replace("{TopicArn}", topicArn).Replace("{QuernArn}", queueArn);

      sqsClient.SetQueueAttributes(new SetQueueAttributesRequest()
      {
          QueueUrl = queueUrl,
          Attributes = new Dictionary<string, string>
          {
              { QueueAttributeName.Policy, policy }
          }
      });
    }

    static void DownloadAnArchive(string archiveId, AmazonGlacierClient client)
    {
      // Initiate job.
      InitiateJobRequest initJobRequest = new InitiateJobRequest()
      {

        VaultName = vaultName,
        JobParameters = new JobParameters()
        {
          Type = "archive-retrieval",
          ArchiveId = archiveId,
          Description = "This job is to download the archive.",
          SNSTopic = topicArn,
        }
      };
      InitiateJobResponse initJobResponse = client.InitiateJob(initJobRequest);
      string jobId = initJobResponse.JobId;

      // Check queue for a message and if job completed successfully, download archive.
      ProcessQueue(jobId, client);
    }

    private static void ProcessQueue(string jobId, AmazonGlacierClient client)
    {
        var receiveMessageRequest = new ReceiveMessageRequest() { QueueUrl = queueUrl, MaxNumberOfMessages = 1 };
        bool jobDone = false;
        while (!jobDone)
        {
            Console.WriteLine("Poll SQS queue");
            ReceiveMessageResponse receiveMessageResponse = sqsClient.ReceiveMessage(receiveMessageRequest);
            if (receiveMessageResponse.Messages.Count == 0)
            {
                Thread.Sleep(10000 * 60);
                continue;
            }
            Console.WriteLine("Got message");
            Message message = receiveMessageResponse.Messages[0];
            Dictionary<string, string> outerLayer = JsonConvert.DeserializeObject<Dictionary<string, string>>(message.Body);
            Dictionary<string, object> fields = JsonConvert.DeserializeObject<Dictionary<string, object>>(outerLayer["Message"]);
            string statusCode = fields["StatusCode"] as string;
            if (string.Equals(statusCode, GlacierUtils.JOB_STATUS_SUCCEEDED, StringComparison.InvariantCultureIgnoreCase))
            {
                long archiveSize = Convert.ToInt64(fields["ArchiveSizeInBytes"]);
                Console.WriteLine("Downloading job output");
                DownloadOutput(jobId, archiveSize, client); // This where we save job output to the specified file location.
            }
            else if (string.Equals(statusCode, GlacierUtils.JOB_STATUS_FAILED, StringComparison.InvariantCultureIgnoreCase))
                Console.WriteLine("Job failed... cannot download the archive.");
            jobDone = true;
            sqsClient.DeleteMessage(new DeleteMessageRequest() { QueueUrl = queueUrl, ReceiptHandle = message.ReceiptHandle });
        }
    }               

    private static void DownloadOutput(string jobId, long archiveSize, AmazonGlacierClient client)
    {
      long partSize = 4 * (long)Math.Pow(2, 20);  // 4 MB.
      using (Stream fileToSave = new FileStream(fileName, FileMode.Create, FileAccess.Write))
      {

        long currentPosition = 0;
        do
        {
          GetJobOutputRequest getJobOutputRequest = new GetJobOutputRequest()
          {
            JobId = jobId,
            VaultName = vaultName
          };

          long endPosition = currentPosition + partSize - 1;
          if (endPosition > archiveSize)
            endPosition = archiveSize;

          getJobOutputRequest.SetRange(currentPosition, endPosition);
          GetJobOutputResponse getJobOutputResponse = client.GetJobOutput(getJobOutputRequest);

          using (Stream webStream = getJobOutputResponse.Body)
          {
            CopyStream(webStream, fileToSave);
          }
          currentPosition += partSize;
        } while (currentPosition < archiveSize);
      }
    }

    public static void CopyStream(Stream input, Stream output)
    {
      byte[] buffer = new byte[65536];
      int length;
      while ((length = input.Read(buffer, 0, buffer.Length)) > 0)
      {
        output.Write(buffer, 0, length);
      }
    }
  }
}
```

# 使用 Python 并行处理下载大型档案
使用 Python 下载大型档案

本主题介绍如何使用 Python 并行处理从 Amazon S3 Glacier（S3 Glacier）下载大型档案。通过此方法，您可以将任意大小的档案分解为可独立处理的较小片段，从而可靠地下载这些档案。

## 概述


本示例中提供的 Python 脚本将执行以下任务：

1. 为通知设置必要的 Amazon 资源（亚马逊 SNS 主题和亚马逊 SQS 队列）

1. 使用 Amazon Glacier 启动档案检索任务

1. 监控 Amazon SQS 队列，获取任务完成通知

1. 将大型档案拆分为可管理的分块

1. 使用多个工作线程并行下载分块

1. 将每个分块保存到磁盘，以备日后重新组装

## 先决条件


开始之前，请确保您已具备以下条件：
+ 已安装 Python 3.6 或更高版本。
+ Amazon 已安装适用于 Python 的 SDK (Boto3)
+ Amazon 为亚马逊 Glacier、Amazon SNS 和亚马逊 SQS 配置了相应权限的证书
+ 有足够的磁盘空间存储下载的档案分块

## 示例：使用 Python 并行处理下载档案


以下 Python 脚本演示了如何使用并行处理从 Amazon Glacier 下载大型档案：

```
import boto3
import time
import json
import jmespath
import re
import concurrent.futures
import os

output_file_path = "output_directory_path"
vault_name = "vault_name"

chunk_size = 1000000000 #1gb - size of chunks for parallel download.
notify_queue_name = 'GlacierJobCompleteNotifyQueue' # SQS queue for Glacier recall notification
chunk_download_queue_name='GlacierChunkReadyNotifyQueue' # SQS queue for chunks
sns_topic_name = 'GlacierRecallJobCompleted' # the SNS topic to be notified when Glacier archive is restored.
chunk_queue_visibility_timeout = 7200 # 2 hours - this may need to be adjusted.
region = 'us-east-1'
archive_id = "archive_id_to_restore"
retrieve_archive = True # set to false if you do not want to restore from Glacier - useful for restarting or parallel processing of the chunk queue.
workers = 12 # the number of parallel worker threads for downloading chunks. 

def setup_queues_and_topic():
    sqs = boto3.client('sqs')
    sns = boto3.client('sns')

    # Create the SNS topic
    topic_response = sns.create_topic(
        Name=sns_topic_name
    )
    topic_arn = topic_response['TopicArn']
    print("Creating the SNS topic " + topic_arn)

    # Create the notification queue
    notify_queue_response = sqs.create_queue(
        QueueName=notify_queue_name,
        Attributes={
            'VisibilityTimeout': '300',  # 5 minutes
            'ReceiveMessageWaitTimeSeconds': '20'  # Enable long polling
        }
    )
    notify_queue_url = notify_queue_response['QueueUrl']
    print("Creating the archive-retrieval notification queue " + notify_queue_url)

    # Create the chunk download queue
    chunk_queue_response = sqs.create_queue(
        QueueName=chunk_download_queue_name,
        Attributes={
            'VisibilityTimeout': str(chunk_queue_visibility_timeout),  # 5 minutes
            'ReceiveMessageWaitTimeSeconds': '0'
        }
    )
    chunk_queue_url = chunk_queue_response['QueueUrl']

    print("Creating the chunk ready notification queue " + chunk_queue_url)


   # Get the ARN for the notification queue
    notify_queue_attributes = sqs.get_queue_attributes(
        QueueUrl=notify_queue_url,
        AttributeNames=['QueueArn']
    )
    notify_queue_arn = notify_queue_attributes['Attributes']['QueueArn']

    # Set up the SNS topic policy on the notification queue
    queue_policy = {
        "Version": "2012-10-17",		 	 	 
        "Statement": [{
            "Sid": "allow-sns-messages",
            "Effect": "Allow",
            "Principal": {"AWS": "*"},
            "Action": "SQS:SendMessage",
            "Resource": notify_queue_arn,
            "Condition": {
                "ArnEquals": {
                    "aws:SourceArn": topic_arn
                }
            }
        }]
    }

    # Set the queue policy
    sqs.set_queue_attributes(
        QueueUrl=notify_queue_url,
        Attributes={
            'Policy': json.dumps(queue_policy)
        }
    )

    # Subscribe the notification queue to the SNS topic
    sns.subscribe(
        TopicArn=topic_arn,
        Protocol='sqs',
        Endpoint=notify_queue_arn
    )

    return {
        'topic_arn': topic_arn,
        'notify_queue_url': notify_queue_url,
        'chunk_queue_url': chunk_queue_url
    }


def split_and_send_chunks(archive_size, job_id,chunk_queue_url):
    ranges = []
    current = 0
    chunk_number = 0

    while current < archive_size:
        chunk_number += 1
        next_range = min(current + chunk_size - 1, archive_size - 1)
        ranges.append((current, next_range, chunk_number))
        current = next_range + 1

    # Send messages to SQS queue
    for start, end, chunk_number in ranges:
        body = {"start": start, "end": end, "job_id": job_id, "chunk_number": chunk_number}
        body = json.dumps(body)
        print("Sending SQS message for range:" + str(body))
        response = sqs.send_message(
            QueueUrl=chunk_queue_url,
            MessageBody=str(body)
        )

def GetJobOutputChunks(job_id, byterange, chunk_number):
    glacier = boto3.client('glacier')
    response = glacier.get_job_output(
        vaultName=vault_name,
        jobId=job_id,
        range=byterange,

    )

    with open(os.path.join(output_file_path,str(chunk_number)+".chunk"), 'wb') as output_file:
        output_file.write(response['body'].read())

    return response

def ReceiveArchiveReadyMessages(notify_queue_url,chunk_queue_url):

    response = sqs.receive_message(
        QueueUrl=notify_queue_url,
        AttributeNames=['All'],
        MaxNumberOfMessages=1,
        WaitTimeSeconds=20,
        MessageAttributeNames=['Message']
    )
    print("Polling archive retrieval job ready queue...")
    # Checking that there is a Messages key before proceeding. No 'Messages' key likely means the queue is empty

    if 'Messages' in response:
        print("Received a message from the archive retrieval job queue")
        jsonresponse = response
        # Loading the string into JSON and checking that ArchiveSizeInBytes key is present before continuing.
        jsonresponse=json.loads(jsonresponse['Messages'][0]['Body'])
        jsonresponse=json.loads(jsonresponse['Message'])
        if 'ArchiveSizeInBytes' in jsonresponse:
            receipt_handle = response['Messages'][0]['ReceiptHandle']    
            if jsonresponse['ArchiveSizeInBytes']:
                archive_size = jsonresponse['ArchiveSizeInBytes']

                print(f'Received message: {response}')      
                if archive_size > chunk_size:
                    split_and_send_chunks(archive_size, jsonresponse['JobId'],chunk_queue_url)

                    sqs.delete_message(
                    QueueUrl=notify_queue_url,
                    ReceiptHandle=receipt_handle)

            else:
                print("No ArchiveSizeInBytes value found in message")
                print(response)

    else:
        print('No messages available in the queue at this time.')

    time.sleep(1)

def ReceiveArchiveChunkMessages(chunk_queue_url):
    response = sqs.receive_message(
        QueueUrl=chunk_queue_url,
        AttributeNames=['All'],
        MaxNumberOfMessages=1,
        WaitTimeSeconds=0,
        MessageAttributeNames=['Message']
    )
    print("Polling archive chunk queue...")
    print(response)
    # Checking that there is a Messages key before proceeding. No 'Messages' key likely means the queue is empty
    if 'Messages' in response:
        jsonresponse = response
        # Loading the string into JSON and checking that ArchiveSizeInBytes key is present before continuing.
        jsonresponse=json.loads(jsonresponse['Messages'][0]['Body'])
        if 'job_id' in jsonresponse: #checking that there is a job id before continuing
            job_id = jsonresponse['job_id']
            byterange = "bytes="+str(jsonresponse['start']) + '-' + str(jsonresponse['end'])
            chunk_number = jsonresponse['chunk_number']
            receipt_handle = response['Messages'][0]['ReceiptHandle']
            if jsonresponse['job_id']:
                print(f'Received message: {response}')
                GetJobOutputChunks(job_id,byterange,chunk_number)
                sqs.delete_message(
                QueueUrl=chunk_queue_url,
                ReceiptHandle=receipt_handle)
    else:
        print('No messages available in the chunk queue at this time.')

def initiate_archive_retrieval(archive_id, topic_arn):
    glacier = boto3.client('glacier')

    job_parameters = {
        "Type": "archive-retrieval",
        "ArchiveId": archive_id,
        "Description": "Archive retrieval job",
        "SNSTopic": topic_arn,
        "Tier": "Bulk"  # You can change this to "Standard" or "Expedited" based on your needs
    }

    try:
        response = glacier.initiate_job(
            vaultName=vault_name,
            jobParameters=job_parameters
        )

        print("Archive retrieval job initiated:")
        print(f"Job ID: {response['jobId']}")
        print(f"Job parameters: {job_parameters}")
        print(f"Complete response: {json.dumps(response, indent=2)}")

        return response['jobId']

    except Exception as e:
        print(f"Error initiating archive retrieval job: {str(e)}")
        raise

def run_async_tasks(chunk_queue_url, workers):
    max_workers = workers  # Set the desired maximum number of concurrent tasks
    with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
        for _ in range(max_workers):
            executor.submit(ReceiveArchiveChunkMessages, chunk_queue_url)

# One time setup of the necessary queues and topics. 
queue_and_topic_atts = setup_queues_and_topic()

topic_arn = queue_and_topic_atts['topic_arn']
notify_queue_url = queue_and_topic_atts['notify_queue_url']
chunk_queue_url = queue_and_topic_atts['chunk_queue_url']

if retrieve_archive:
    print("Retrieving the defined archive... The topic arn we will notify when recalling the archive is: "+topic_arn)
    job_id = initiate_archive_retrieval(archive_id, topic_arn)
else:
    print("Retrieve archive is false, polling queues and downloading only.")

while True:
   ReceiveArchiveReadyMessages(notify_queue_url,chunk_queue_url)
   run_async_tasks(chunk_queue_url,workers)
```

## 使用脚本


要使用此脚本，请按照下列步骤操作：

1. 将脚本中的占位符值替换为您的具体信息：
   + *output\$1file\$1path*: 用于保存区块文件的目录
   + *vault\$1name*: 你的 S3 Glacier 保管库的名称
   + *notify\$1queue\$1name*: 作业通知队列的名称
   + *chunk\$1download\$1queue\$1name*: 区块下载队列的名称
   + *sns\$1topic\$1name*: SNS 主题的名称
   + *region*: Amazon 您的保管库所在的地区
   + *archive\$1id*: 要检索的档案的 ID

1. 运行脚本：

   ```
   python download_large_archive.py
   ```

1. 下载完所有分块后，您可以使用如下命令将它们合并为一个文件：

   ```
   cat /path/to/chunks/*.chunk > complete_archive.file
   ```

## 重要注意事项


使用此脚本时，请注意以下几点：
+ S3 Glacier 档案检索可能需要几个小时才能完成，具体取决于所选的检索层级。
+ 此脚本无限期运行，持续轮询队列。您可能需要根据自己的特定需求添加终止条件。
+ 确保您有足够的磁盘空间来存储所有档案分块。
+ 如果脚本中断，您可以通过 `retrieve_archive=False` 重新启动脚本，以继续下载分块，而无需启动新的检索任务。
+ 根据您的网络带宽*chunk\$1size*和系统资源调整和*workers*参数。
+ 亚马逊 S3 检索、亚马逊 SNS 和亚马逊 SQS 的使用收取标准 Amazon 费用。

# 使用 REST API 下载档案
使用 REST 下载档案

**使用 REST API 下载档案**

下载档案是一个分为两个步骤的流程。

1. 启动 `archive-retrieval` 类型的任务。有关更多信息，请参阅[启动任务（POST jobs）](api-initiate-job-post.md)。

1. 任务完成后，下载档案数据。有关更多信息，请参阅[获取任务输出（GET output）](api-job-output-get.md)。

# 使用 Amazon Glacier 下载档案 Amazon CLI
使用下载档案 Amazon CLI

你可以使用 () 在 Amazon Glacier（Amazon Glacier Amazon CLI）中 Amazon Command Line Interface 下载档案。

**Topics**
+ [

## （先决条件）设置 Amazon CLI
](#Creating-Vaults-CLI-Setup)
+ [

## 示例：使用下载档案 Amazon CLI
](#Downloading-Archives-CLI-Implementation)

## （先决条件）设置 Amazon CLI


1. 下载并配置 Amazon CLI。有关说明，请参阅《Amazon Command Line Interface 用户指南》**中的以下主题：

    [正在安装 Amazon Command Line Interface](https://docs.amazonaws.cn/cli/latest/userguide/installing.html) 

   [正在配置 Amazon Command Line Interface](https://docs.amazonaws.cn/cli/latest/userguide/cli-chap-getting-started.html)

1. 在命令提示符下输入以下命令来验证您的 Amazon CLI 设置。这些命令没有显式提供凭证，因此将使用默认配置文件的凭证。
   + 尝试使用 help 命令。

     ```
     aws help
     ```
   + 要获取已配置账户上 Amazon Glacier 文件库的列表，请使用 `list-vaults` 命令。*123456789012*用您的 Amazon Web Services 账户 身份证替换。

     ```
     aws glacier list-vaults --account-id 123456789012
     ```
   + 要查看的当前配置数据 Amazon CLI，请使用`aws configure list`命令。

     ```
     aws configure list
     ```

## 示例：使用下载档案 Amazon CLI

**注意**  
要下载档案，您必须知道档案 ID。步骤 1-4 将检索档案 ID。如果您已经知道要下载的档案 ID，请跳至第 5 步。

1. 使用 `initiate-job` 命令启动清单检索任务。清单报告将列出您的档案 ID。

   ```
   aws glacier initiate-job --vault-name awsexamplevault --account-id 111122223333 --job-parameters="{\"Type\":\"inventory-retrieval\"}"
   ```

    预期输出：

   ```
   {
       "location": "/111122223333/vaults/awsexamplevault/jobs/*** jobid ***", 
       "jobId": "*** jobid ***"
   }
   ```

1. 使用 `describe-job` 命令检查上一个 `` 任务的状态。

   ```
   aws glacier describe-job --vault-name awsexamplevault --account-id 111122223333 --job-id *** jobid ***
   ```

    预期输出：

   ```
   {
       "InventoryRetrievalParameters": {
           "Format": "JSON"
       }, 
       "VaultARN": "*** vault arn ***", 
       "Completed": false, 
       "JobId": "*** jobid ***", 
       "Action": "InventoryRetrieval", 
       "CreationDate": "*** job creation date ***", 
       "StatusCode": "InProgress"
   }
   ```

1. 等待任务完成。

   您必须等到任务输出已作好供您下载的准备。如果您在文件库中设置了通知配置，或者在启动任务时指定了 Amazon Simple Notification Service（Amazon SNS）主题，则 Amazon Glacier 会在完成任务后向该主题发送消息。

   您可以设置文件库的特定事件的通知配置。有关更多信息，请参阅[在 Amazon Glacier 中配置文件库通知](configuring-notifications.md)。只要发生特定事件，Amazon Glacier 就会向指定的 SNS 主题发送消息。

1. 完成后，使用 `get-job-output` 命令将检索任务下载到文件 `output.json`。此文件将包含您的档案 ID。

   ```
   aws glacier get-job-output --vault-name awsexamplevault --account-id 111122223333 --job-id *** jobid *** output.json
   ```

   此命令会生成一个包含以下字段的文件。

   ```
   {
   "VaultARN":"arn:aws:glacier:region:111122223333:vaults/awsexamplevault",
   "InventoryDate":"*** job completion date ***",
   "ArchiveList":[
   {"ArchiveId":"*** archiveid ***",
   "ArchiveDescription":*** archive description (if set) ***,
   "CreationDate":"*** archive creation date ***",
   "Size":"*** archive size (in bytes) ***",
   "SHA256TreeHash":"*** archive hash ***"
   }
   {"ArchiveId":
   ...
   ]}
   ```

1. 使用 `initiate-job` 命令启动检索文件库中每个档案的过程。您需要指定任务参数，如下面所示的 `archive-retrieval`。

   ```
   aws glacier initiate-job --vault-name awsexamplevault --account-id 111122223333 --job-parameters="{\"Type\":\"archive-retrieval\",\"ArchiveId\":\"*** archiveId ***\"}"
   ```

1. 等待 `archive-retrieval` 任务完成。使用 `describe-job` 命令检查上一个命令的状态。

   ```
   aws glacier describe-job --vault-name awsexamplevault --account-id 111122223333 --job-id *** jobid ***
   ```

1. 完成上述任务后，使用 `get-job-output` 命令下载您的档案。

   ```
   aws glacier get-job-output --vault-name awsexamplevault --account-id 111122223333 --job-id *** jobid *** output_file_name
   ```