into the void

ソフトウェアに関する雑多な調査日記

Amazon Glacierを使ってiPhotoの写真をバックアップしてみる(其の壱)

iPhotoのライブラリを圧縮する

去年までの写真が入っているiPhotoのライブラリをフォルダごとtarで固めて圧縮した。サイズは90GB程度。Glacierなら毎月100円程度で保存してくれるはず。

AWSのアカウントを作る

AWSを使ったことがなかったので新しくアカウントを作った。クレジットカード番号を入力したあと、Amazonからの携帯への電話着信による認証をおこなってアカウントの準備は完了。

Glacierを使うための準備をする

下記のサイトの使い方をみながらセットアップをした。
http://aws.amazon.com/jp/glacier/
まずボールトなるものをAWSマネージメントコンソールで作った。RegionはTokyo。Notificationの設定を有効にした。次にマネージメントコンソールのSNSのページで作ったNotificationの通知先を設定。今回はメールで通知にした。
あと、Eclipseでコーディングしたかったので、Java EE Developer版の3.7をダウンロードしてインストール。AWS Toolkitをアドインしてコーディングの準備も完了。

Glacierを使ってみる

JAVAAPIをつかって適当なファイルをアップロードしてみる。下記のサイトにサンプルコードがあるのでほぼそのまま使った。
http://docs.amazonwebservices.com/amazonglacier/latest/dev/uploading-an-archive.html
アップロード方法には下記の2種類があるらしい。

  • Uploading an Archive in a Single Operation
  • Uploading Large Archives in Parts

前者はファイルを一回のアップロード操作で送信する方法。小さなファイル向け。100MB以下推奨。最大4GB。後者は大きなファイルを分割してアップロードする方法。分割サイズは最大4GB。
今回は4GBを超えているので後者方法が必須。
とはいえまずはシンプルな前者方法を試してみる。アップロードするのはhello.txtというテキストファイル。
サンプルコードから変更したのはendpointのURLくらい。tokyo region用にかえた。

package myaws.glaceir;

import java.io.File;
import java.io.IOException;
import java.util.Date;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.glacier.AmazonGlacierClient;
import com.amazonaws.services.glacier.transfer.ArchiveTransferManager;
import com.amazonaws.services.glacier.transfer.UploadResult;

public class TestSingleOperation {
	public static String vaultName = "Test_Tokyo01";
	public static String archiveToUpload = "/Users/xxxx/glacier/test/hello.txt";
	
	public static AmazonGlacierClient client;
	
	public static void main(String args[]) throws IOException{
		AWSCredentials credentials = new PropertiesCredentials
				(TestSingleOperation.class.getResourceAsStream("AwsCredentials.properties"));
		client = new AmazonGlacierClient(credentials);
		client.setEndpoint("https://glacier.ap-northeast-1.amazonaws.com");
		
		try{
			ArchiveTransferManager manager = new ArchiveTransferManager(client, credentials);
			UploadResult result = manager.upload(vaultName, "my archive " + (new Date()), new File(archiveToUpload));
			System.out.println("Archive ID: " + result.getArchiveId());
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

実行するとArchive IDが表示されて無事にアップロードできた様子。SNSからのEメールによるNotificationもきていた。マネージメントコンソールでアップロードが確認できるのは一日後ということでとりあえず待ってみる。

次に分割アップロードの方で18Mのtar.gzファイルを送信してみる。こっちもすんなりうまくいった。サンプルコードとかえたのはendpointのURLとデバッグ文の追加。

package myaws.glaceir;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.LinkedList;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.glacier.AmazonGlacierClient;
import com.amazonaws.services.glacier.TreeHashGenerator;
import com.amazonaws.services.glacier.model.CompleteMultipartUploadRequest;
import com.amazonaws.services.glacier.model.CompleteMultipartUploadResult;
import com.amazonaws.services.glacier.model.InitiateMultipartUploadRequest;
import com.amazonaws.services.glacier.model.InitiateMultipartUploadResult;
import com.amazonaws.services.glacier.model.UploadMultipartPartRequest;
import com.amazonaws.services.glacier.model.UploadMultipartPartResult;
import com.amazonaws.util.BinaryUtils;



public class TestMultiPartOperation {

	public static String vaultName = "Test_Tokyo02";
	public static String partSize = "1048576"; // 1MB
	public static String archiveFilePath = 
			"/Users/xxxx/glacier/test/testphotos.tar.gz";
	public static AmazonGlacierClient client;

	public static void main(String args[]) throws Exception{
		AWSCredentials credentials = new PropertiesCredentials(
				TestMultiPartOperation.class.getResourceAsStream("AwsCredentials.properties"));
		client = new AmazonGlacierClient(credentials);
		client.setEndpoint("https://glacier.ap-northeast-1.amazonaws.com");
		
		try{
			System.out.println("Uploading an archive.");
			String uploadId = initialMultipartUpload();
			String checksum = uploadParts(uploadId);
			String archiveId = CompleteMultiPartUpload(uploadId, checksum);
			System.out.println("Completed an archive. Archive ID: " + archiveId);
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	private static String initialMultipartUpload(){
		InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest();
		request.withVaultName(vaultName);
		request.withArchiveDescription("my archive " + (new Date()));
		request.withPartSize(partSize);
		
		InitiateMultipartUploadResult result = client.initiateMultipartUpload(request);
		System.out.println("Upload ID : " + result.getUploadId());
		return result.getUploadId();
	}
	
	private static String uploadParts(String uploadId) throws Exception{
		int filePosition = 0;
		long currentPosition = 0;
		byte[] buffer = new byte[Integer.valueOf(partSize)];
		List<byte[]> binaryChecksums = new LinkedList<byte[]>();
		
		File file = new File(archiveFilePath);
		FileInputStream fis = new FileInputStream(file);
		String contentRange;
		int read = 0;
		int loopCnt = 1;
		while(currentPosition < file.length()){
			System.out.println();
			System.out.println("Loop Count   = " + loopCnt);
			loopCnt++;
			read = fis.read(buffer, filePosition, buffer.length);
			if(read == -1) { break; }
			byte[] byteRead = Arrays.copyOf(buffer, read);
			
			contentRange = String.format("bytes %s-%s/*", currentPosition, currentPosition + read - 1);
			String checksum = TreeHashGenerator.calculateTreeHash(new ByteArrayInputStream(byteRead));
			System.out.println("checksum     = " + checksum);
			byte[] binaryChecksum = BinaryUtils.fromHex(checksum);
			binaryChecksums.add(binaryChecksum);
			System.out.println("contentRange = " + contentRange);
			
			UploadMultipartPartRequest partRequest = new UploadMultipartPartRequest();
			partRequest.withVaultName(vaultName);
			partRequest.withBody(new ByteArrayInputStream(byteRead));
			partRequest.withChecksum(checksum);
			partRequest.withRange(contentRange);
			partRequest.withUploadId(uploadId);
			
			UploadMultipartPartResult partResult = client.uploadMultipartPart(partRequest);
			System.out.println("Part uploaded, checksum = " + partResult.getChecksum());
			
			currentPosition = currentPosition + read;
		}
		String checksum = TreeHashGenerator.calculateTreeHash(binaryChecksums);
		System.out.println("All checksum = " + checksum);
		return checksum;
	}
	
	private static String CompleteMultiPartUpload(String uploadId, String checksum) throws Exception{
		File file = new File(archiveFilePath);
		
		CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest();
		request.withVaultName(vaultName);
		request.withUploadId(uploadId);
		request.withChecksum(checksum);
		request.withArchiveSize(String.valueOf(file.length()));
		
		CompleteMultipartUploadResult result = client.completeMultipartUpload(request);
		return result.getLocation();
	}
	
}

明日以降、アップロード完了を確認して、それからダウンロードしてみる。
そのあとで、iPhotoのバックアップデータをアップロードすることにする。