

 **此頁面僅適用於使用 Vaults 和 2012 年原始 REST API 的 Amazon Glacier 服務的現有客戶。**

如果您要尋找封存儲存解決方案，建議您在 Amazon Glacier Instant Retrieval、S3 Glacier Flexible Retrieval 和 S3 Glacier Deep Archive 中使用 Amazon Glacier 儲存類別。 Amazon S3 若要進一步了解這些儲存選項，請參閱 [Amazon Glacier 儲存類別](https://aws.amazon.com/s3/storage-classes/glacier/)。

Amazon Glacier （原始獨立保存庫型服務） 不再接受新客戶。Amazon Glacier 是一項獨立服務，具有自己的 APIs，可將資料存放在保存庫中，並與 Amazon S3 和 Amazon S3 Glacier 儲存類別不同。您現有的資料將在 Amazon Glacier 中無限期保持安全且可存取。不需要遷移。對於低成本、長期的封存儲存， AWS 建議使用 [Amazon S3 Glacier 儲存類別](https://aws.amazon.com/s3/storage-classes/glacier/)，透過 S3 儲存貯體型 APIs、完整 AWS 區域 可用性、降低成本 AWS 和服務整合，提供卓越的客戶體驗。如果您想要增強功能，請考慮使用我們的解決方案指南，將資料從 Amazon S3 Glacier 保存庫傳輸至 Amazon S3 Glacier 儲存類別，以遷移至 Amazon S3 Glacier 儲存類別。 [AWS Amazon Glacier Amazon S3 ](https://aws.amazon.com/solutions/guidance/data-transfer-from-amazon-s3-glacier-vaults-to-amazon-s3/)

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

# 在 Amazon Glacier 中下載封存
<a name="downloading-an-archive"></a>

Amazon Glacier 提供管理主控台，可用來建立和刪除保存庫。不過，您無法使用 管理主控台從 Amazon Glacier 下載封存。若要下載資料，例如相片、影片和其他文件，您必須使用 AWS Command Line Interface (AWS CLI) 或編寫程式碼來提出請求，方法是直接使用 REST API 或使用 AWS SDKs。

如需搭配 使用 Amazon Glacier 的詳細資訊 AWS CLI，請參閱 [AWS CLI Amazon Glacier 的參考](https://docs.aws.amazon.com/cli/latest/reference/glacier/index.html)。若要安裝 AWS CLI，請參閱 [AWS Command Line Interface](https://aws.amazon.com/cli/)。下列主題說明如何使用 適用於 Java 的 AWS SDK、 適用於 .NET 的 AWS SDK和 Amazon Glacier REST API 將封存下載至 Amazon Glacier。

**Topics**
+ [擷取 Amazon Glacier Archives](downloading-an-archive-two-steps.md)
+ [使用 在 Amazon Glacier 中下載封存 適用於 Java 的 AWS SDK](downloading-an-archive-using-java.md)
+ [使用 在 Amazon Glacier 中下載封存 適用於 .NET 的 AWS SDK](downloading-an-archive-using-dotnet.md)
+ [使用 Python 平行處理下載大型封存](downloading-large-archive-parallel-python.md)
+ [使用 REST API 下載封存](downloading-an-archive-using-rest.md)
+ [使用 在 Amazon Glacier 中下載封存 AWS CLI](downloading-an-archive-using-cli.md)

# 擷取 Amazon Glacier Archives
<a name="downloading-an-archive-two-steps"></a>

從 Amazon Glacier 擷取封存是一種非同步操作，您會先啟動任務，然後在任務完成後下載輸出。若要啟動封存擷取任務，您可以使用 [啟動任務 (POST 任務)](api-initiate-job-post.md) REST API 操作或 AWS CLI或 AWS SDKs中的同等操作。

**Topics**
+ [封存擷取選項](#api-downloading-an-archive-two-steps-retrieval-options)
+ [遠端封存擷取](#downloading-an-archive-range)

從 Amazon Glacier 擷取封存是一個兩步驟的程序。以下是此程序的概觀。

**擷取封存**

1. 啟動封存擷取任務。

   1. 取得您想要擷取的封存 ID。您可以從文件庫的清查取得存檔 ID。您可以使用 REST API、 AWS CLI或 AWS SDKs 取得封存 ID。如需詳細資訊，請參閱[在 Amazon Glacier 中下載保存庫庫存](vault-inventory.md)。

   1. 使用 [啟動任務 (POST 任務)](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 輸出)](api-job-output-get.md) 作業下載位元組。

   您可以下載所有位元組，或指定位元組範圍，以僅下載任務輸出的一部分。對於較大的輸出，下載區塊形式的輸出對於發生下載失敗的情況有所幫助，例如網路失敗。如果您在單一請求中取得任務輸出，並且發生網路失敗，此時您必須重頭開始重新下載輸出。不過，如果您以區塊形式下載輸出，當發生任何失敗情況時，您只需重新開始下載較小部分，而不是整個輸出。

Amazon Glacier 必須先完成任務，才能取得其輸出。完成工作之後至少 24 小時內，工作不會過期，意思是您可以在工作完成後 24 小時內下載輸出。還原可能會在任務完成後 24 小時後隨時過期。若要判斷您的任務是否已完成後，您可使用以下其中一個選項來檢查其狀態：
+ **等待任務完成通知** – 您可以指定 Amazon Simple Notification Service (Amazon SNS) 主題，Amazon Glacier 可以在任務完成後發佈通知。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 作業所取得的資訊相同。

## 封存擷取選項
<a name="api-downloading-an-archive-two-steps-retrieval-options"></a>

啟動工作來擷取封存時，您可以根據存取時間和成本需求，指定以下其中一項擷取選項。如需擷取定價的資訊，請參閱 [Amazon Glacier 定價](https://aws.amazon.com/s3/glacier/pricing/)。
+ **快速**：快速擷取可讓您在偶爾需要緊急請求還原封存時，能快速存取在 S3 Glacier Flexible Retrieval 儲存體類別或 S3 Intelligent-Tiering Archive Access 層中存放的資料。用於規模幾乎最大的封存 (250 MB 以上) 時，使用快速擷取所存取的資料，通常在 1-5 分鐘內即可使用。佈建容量可確保快速擷取在需要時有可用的擷取容量。如需詳細資訊，請參閱[佈建的容量](#api-downloading-an-archive-two-steps-retrieval-expedited-capacity)。
+ **標準**：標準擷取可讓您在幾小時內存取任何封存。標準擷取通常會於 3-5 小時內完成。未指定擷取選項時，擷取請求的預設選項會是「標準」。
+ **大量** – 大量擷取是成本最低的 Amazon Glacier 擷取選項，您可以在一天中以廉價的方式擷取大量資料，甚至是 PB。大量擷取通常會於 5-12 小時內完成。

下表摘要說明封存擷取選項。如需定價的詳細資訊，請參閱 [Amazon Glacier 定價。](https://aws.amazon.com/s3/glacier/pricing/)


| 服務 | 快速 | 標準 | 大批 | 
| --- | --- | --- | --- | 
|  Amazon Glacier  |  1 - 5 分鐘  |  3 - 5 小時  |  5 - 12 小時  | 

若要進行 `Expedited`、 `Standard`或 `Bulk`擷取，請將 [https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPOSTrestore.html](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPOSTrestore.html) REST API 操作請求中的`Tier`請求元素設定為您想要的選項，或 AWS Command Line Interface (AWS CLI) AWS SDKs中的對等項目。若已購買佈建的容量，則所有快速擷取都會自動透過佈建的容量提供服務。

### 佈建的容量
<a name="api-downloading-an-archive-two-steps-retrieval-expedited-capacity"></a>

佈建容量有助於確保快速擷取在需要時有可用的擷取容量。每個容量單位都提供每 5 分鐘至少可以執行三次「快速」擷取，並提供最多每秒 150 MB (MBps) 的擷取輸送量。

如果工作負載需要非常穩定且可預測的資料子集即時存取，我們建議您購買佈建的擷取容量。但即使沒有佈建的容量，通常仍可進行快速擷取，除非您要擷取的需求量不尋常地高，但此情況很罕見。但是若您需要無論情況如何，皆能存取快速擷取，則必須購買佈建的擷取容量。

#### 購買佈建容量
<a name="downloading-an-archive-purchase-provisioned-capacity"></a>

您可以使用 Amazon Glacier 主控台、[購買佈建容量 (POST 佈建的容量)](api-PurchaseProvisionedCapacity.md)REST API 操作、 AWS SDKs 或 購買佈建容量單位 AWS CLI。如需佈建容量定價資訊，請參閱 [Amazon Glacier 定價](https://aws.amazon.com/s3/glacier/pricing/)。

佈建容量單位會持續一個月，從購買的日期和時間開始。

如果開始日期是在某個月的第 31 天，過期日期則是下個月的最後一天。例如，如果開始日期是 8 月 31 日，過期日期則是 9 月 30 日。如果開始日期是 1 月 31 日，過期日期則是 2 月 28 日。

**使用 Amazon Glacier 主控台購買佈建容量**

1.  登入 AWS 管理主控台 ，並在 [https://console.aws.amazon.com/glacier/home](https://console.aws.amazon.com/glacier/home)：// 開啟 Amazon Glacier 主控台。

1. 在左側的導覽窗格中，選擇**資料擷取設定**。

1. 在**佈建容量單位 (PCU)** 下，選擇**購買 PCU**。這時會顯示**購買 PCU** 對話方塊。

1. 如果您想要購買佈建的容量，請在**確認購買**方塊中輸入 **confirm**。

1.  選擇**購買 PCU**。

## 遠端封存擷取
<a name="downloading-an-archive-range"></a>

當您從 Amazon Glacier 擷取存檔時，您可以選擇性指定要擷取之封存範圍或部分。預設值是擷取整個存檔。指定位元組範圍，可在您執行下列動作時派上用場：
+ **管理您的資料下載** – Amazon Glacier 允許在擷取請求完成後 24 小時內下載擷取的資料。因此，您可能希望只擷取封存部分，讓您可以在指定下載時段內管理下載的排程。
+ **擷取大型檔案的目標部分**：例如，假設您之前已彙總多個檔案，並以單一封存形式將檔案上傳，而現在您想要擷取幾個檔案。在這種情況下，您可以使用一個擷取請求，指定包含您需要的檔案的封存範圍。或者，您可以啟動多個擷取請求，其中每一個都附帶有一或多個檔案的範圍。

使用範圍擷取啟動擷取任務時，您必須提供符合百萬位元組範圍。換言之，位元組範圍以零開始 (檔案的開頭)，或在之後以 1 MB 為間隔 (1 MB、2 MB、3 MB 等)。

範圍結尾可以是封存的結尾，或任何大於您的範圍開頭的 1 MB 間隔。此外，如果您要在下載資料後取得檢查總和值 (擷取工作完成後)，您在工作啟動請求的範圍，也必須符合樹雜湊。您可以使用檢查總和，來協助確保資料在傳輸期間未遭到損毀。如需有關符合百萬位元組與符合樹雜湊的詳細資訊，請參閱 [下載資料時接收檢查總和](checksum-calculations-range.md)。

# 使用 在 Amazon Glacier 中下載封存 適用於 Java 的 AWS SDK
<a name="downloading-an-archive-using-java"></a>

適用於 Java 的 Amazon 開發套件提供的[高階和低階 API](using-aws-sdk.md) 都提供了下載封存的方法。

**Topics**
+ [使用 的高階 API 下載封存 適用於 Java 的 AWS SDK](#downloading-an-archive-using-java-highlevel-api)
+ [使用 的低階 API 下載封存 適用於 Java 的 AWS SDK](#downloading-an-archive-using-java-lowlevel-api)

## 使用 的高階 API 下載封存 適用於 Java 的 AWS SDK
<a name="downloading-an-archive-using-java-highlevel-api"></a>

高階 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 的 AWS SDK
<a name="download-archives-java-highlevel-example"></a>

以下 Java 程式碼範例從美國西部 (奧勒岡) 區域 (`us-west-2`) 中的保存庫 (`examplevault`) 下載封存。

如需執行此範例的逐步說明，請參閱 [使用 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 的 AWS SDK
<a name="downloading-an-archive-using-java-lowlevel-api"></a>

以下是使用 適用於 Java 的 AWS SDK 低階 API 來擷取保存庫庫存的步驟。

 

1. 建立 `AmazonGlacierClient` 類別的執行個體 (用戶端)。

   您需要指定要 AWS 從中下載封存的區域。您使用此用戶端執行的所有操作都會套用到該 AWS 區域。

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傳送的檢查總和進行比較。

   對於指定可選範圍的封存擷取任務，當您取得任務說明時，它將包含您正在擷取的範圍的檢查總和 (SHA256TreeHash)。您可以使用此值進一步驗證稍後下載的整個位元組範圍的準確性。例如，如果啟動任務以擷取樹狀雜湊符合的封存範圍，然後以區塊的方式下載輸出，以便每個 `GetJobOutput` 請求傳回檢查總和，則可以計算在用戶端上下載的每個部分的檢查總和，然後計算樹狀雜湊。您可以將其與 Amazon Glacier 傳回的檢查總和進行比較，以回應您的描述任務請求，以確認您下載的整個位元組範圍與存放在 Amazon Glacier 中的位元組範圍相同。

    如需運作範例，請參閱 [範例 2：使用 的低階 API 擷取封存 適用於 Java 的 AWS SDK- 在區塊中下載輸出](#downloading-an-archive-with-range-using-java-example)。

### 範例 1：使用 的低階 API 擷取封存 適用於 Java 的 AWS SDK
<a name="downloading-an-archive-using-java-example"></a>

以下 Java 程式碼範例從指定的保存庫下載封存。任務完成後，該範例將在單一 `getJobOutput` 呼叫中下載整個輸出。如需有關以區塊形式下載輸出的範例，請參閱 [範例 2：使用 的低階 API 擷取封存 適用於 Java 的 AWS SDK- 在區塊中下載輸出](#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：使用 的低階 API 擷取封存 適用於 Java 的 AWS SDK- 在區塊中下載輸出
<a name="downloading-an-archive-with-range-using-java-example"></a>

下列 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 的 AWS SDK
<a name="downloading-an-archive-using-dotnet"></a>

適用於 .NET 的 Amazon 開發套件提供的[高階和低階 API](using-aws-sdk.md) 都提供了下載封存的方法。

**Topics**
+ [使用 的高階 API 下載封存 適用於 .NET 的 AWS SDK](#downloading-an-archive-using-dotnet-highlevel-api)
+ [使用 的低階 API 下載封存 適用於 .NET 的 AWS SDK](#downloading-an-archive-using-dotnet-lowlevel-api)

## 使用 的高階 API 下載封存 適用於 .NET 的 AWS SDK
<a name="downloading-an-archive-using-dotnet-highlevel-api"></a>

高階 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 的 AWS SDK
<a name="download-archives-dotnet-highlevel-example"></a>

以下 C\$1 程式碼範例從美國西部 (奧勒岡) 區域中的保存庫 (`examplevault`) 下載封存。

如需執行此範例的逐步說明，請參閱 [執行程式碼範例](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.rproxy.govskope.us.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 的 AWS SDK
<a name="downloading-an-archive-using-dotnet-lowlevel-api"></a>

以下是使用 的低階 API 下載 Amazon Glacier (Amazon Glacier) 封存的步驟 適用於 .NET 的 AWS SDK。

1. 建立 `AmazonGlacierClient` 類別的執行個體 (用戶端)。

   您需要指定要從中下載封存 AWS 的區域。您使用此用戶端執行的所有操作都會套用到該 AWS 區域。

1. 執行 `archive-retrieval` 方法以啟動 `InitiateJob` 任務。

   您可以透過建立 `InitiateJobRequest`類別的執行個體，提供任務資訊，例如您要下載之封存的封存 ID，以及您希望 Amazon Glacier 發佈任務完成訊息的選用 Amazon SNS 主題。 Amazon Glacier 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 SNS for Amazon Glacier 來發佈訊息。

   您也可以呼叫 `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：使用 的低階 API 擷取封存 適用於 .NET 的 AWS SDK- 在區塊中下載輸出](#creating-vaults-sdk-dotnet-example2)。

### 範例 1：使用 的低階 API 擷取封存 適用於 .NET 的 AWS SDK
<a name="creating-vaults-sdk-dotnet-example-retrieve"></a>

以下 C\$1 程式碼範例從指定的保存庫下載封存。任務完成後，該範例將在單一 `GetJobOutput` 呼叫中下載整個輸出。如需有關以區塊形式下載輸出的範例，請參閱 [範例 2：使用 的低階 API 擷取封存 適用於 .NET 的 AWS SDK- 在區塊中下載輸出](#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.rproxy.govskope.us.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：使用 的低階 API 擷取封存 適用於 .NET 的 AWS SDK- 在區塊中下載輸出
<a name="creating-vaults-sdk-dotnet-example2"></a>

下列 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.rproxy.govskope.us.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 平行處理下載大型封存
<a name="downloading-large-archive-parallel-python"></a>

本主題說明如何使用 Python 平行處理從 Amazon S3 Glacier (S3 Glacier) 下載大型封存。此方法可讓您將任何大小的封存可靠地下載，方法是將其分成可獨立處理的較小部分。

## 概觀
<a name="downloading-large-archive-python-overview"></a>

此範例中提供的 Python 指令碼會執行下列任務：

1. 設定通知的必要 AWS 資源 (Amazon SNS 主題和 Amazon SQS 佇列）

1. 使用 Amazon Glacier 啟動封存擷取任務

1. 監控任務完成通知的 Amazon SQS 佇列

1. 將大型封存分割為可管理區塊

1. 使用多個工作者執行緒平行下載區塊

1. 將每個區塊儲存至磁碟，以供日後重組

## 先決條件
<a name="downloading-large-archive-python-prerequisites"></a>

開始之前，請確定您已：
+ 已安裝 Python 3.6 或更新版本
+ AWS 已安裝適用於 Python (Boto3) 的 SDK
+ AWS 針對 Amazon Glacier、Amazon SNS 和 Amazon SQS 設定具有適當許可的憑證
+ 有足夠的磁碟空間來存放下載的封存區塊

## 範例：使用 Python 平行處理下載封存
<a name="downloading-large-archive-python-code"></a>

下列 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)
```

## 使用指令碼
<a name="downloading-large-archive-python-usage"></a>

若要使用此指令碼，請遵循下列步驟：

1. 將指令碼中的預留位置值取代為您的特定資訊：
   + *output\$1file\$1path*：儲存區塊檔案的目錄
   + *vault\$1name*：S3 Glacier 保存庫的名稱
   + *notify\$1queue\$1name*：任務通知佇列的名稱
   + *chunk\$1download\$1queue\$1name*：區塊下載佇列的名稱
   + *sns\$1topic\$1name*：SNS 主題的名稱
   + *region*：region 您的保存庫所在的 AWS 區域
   + *archive\$1id*：要擷取的封存 ID

1. 執行 指令碼：

   ```
   python download_large_archive.py
   ```

1. 下載所有區塊之後，您可以使用下列命令將它們合併為單一檔案：

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

## 重要考量
<a name="downloading-large-archive-python-considerations"></a>

使用此指令碼時，請記住下列事項：
+ 從 S3 Glacier 擷取封存可能需要數小時才能完成，具體取決於選取的擷取層。
+ 指令碼會無限期執行，持續輪詢佇列。您可能想要根據您的特定需求新增終止條件。
+ 請確定您有足夠的磁碟空間來存放封存的所有區塊。
+ 如果指令碼中斷，您可以使用 重新啟動指令碼`retrieve_archive=False`，以繼續下載區塊，而無需啟動新的擷取任務。
+ 根據您的網路頻寬和系統資源調整 *chunk\$1size* 和*工作者*參數。
+ Amazon S3 擷取、Amazon SNS 和 Amazon SQS 用量需支付標準 AWS 費用。

# 使用 REST API 下載封存
<a name="downloading-an-archive-using-rest"></a>

**使用 REST API 下載封存**

下載封存是兩個步驟程序。

1. 啟動 `archive-retrieval` 類型的任務。如需詳細資訊，請參閱[啟動任務 (POST 任務)](api-initiate-job-post.md)。

1. 工作完成後，下載封存資料。如需詳細資訊，請參閱[「取得任務輸出」 (GET 輸出)](api-job-output-get.md)。

# 使用 在 Amazon Glacier 中下載封存 AWS CLI
<a name="downloading-an-archive-using-cli"></a>

您可以使用 AWS Command Line Interface () 在 Amazon Glacier (Amazon Glacier) 中下載封存AWS CLI。

**Topics**
+ [（先決條件） 設定 AWS CLI](#Creating-Vaults-CLI-Setup)
+ [範例：使用 下載封存 AWS CLI](#Downloading-Archives-CLI-Implementation)

## （先決條件） 設定 AWS CLI
<a name="Creating-Vaults-CLI-Setup"></a>

1. 下載和設定 AWS CLI。如需相關指示，請參閱《AWS Command Line Interface 使用者指南》**中的下列主題：

    [安裝 AWS Command Line Interface](https://docs.aws.amazon.com/cli/latest/userguide/installing.html) 

   [設定 AWS Command Line Interface](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html)

1. 在命令提示中輸入下列命令來驗證您的 AWS CLI 設定。這些命令不會明確提供登入資料，因此會使用預設描述檔的登入資料。
   + 嘗試使用幫助命令。

     ```
     aws help
     ```
   + 若要取得已設定帳戶上的 Amazon Glacier 保存庫清單，請使用 `list-vaults`命令。將 *123456789012* 取代為您的 AWS 帳戶 ID。

     ```
     aws glacier list-vaults --account-id 123456789012
     ```
   + 若要查看 目前的組態資料 AWS CLI，請使用 `aws configure list`命令。

     ```
     aws configure list
     ```

## 範例：使用 下載封存 AWS CLI
<a name="Downloading-Archives-CLI-Implementation"></a>
**注意**  
為了下載封存，您必須知道封存 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
   ```