ブラウザからS3に直接ファイルをアップロードする

CloudデザインパターンのDirect Object UploadパターンをJavaで作る際のメモ
CDP:Direct Object Uploadパターン - AWS-CloudDesignPattern

Browser Uploads to S3 using HTML POST Forms : Articles & Tutorials : Amazon Web Services
こちらを参考にHTMLを作成する

<html> 
  <head>
    <title>S3 POST Form</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>

  <body> 
    <form action="https://【バケット名】.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
      <input type="hidden" name="key" value="【パス(任意)】${filename}">
      <input type="hidden" name="AWSAccessKeyId" value="【AWSアクセスキー】"> 
      <input type="hidden" name="acl" value="private"> 
      <input type="hidden" name="success_action_redirect" value="【リダイレクト先URL】">
      <input type="hidden" name="policy" value="【ポリシー】">
      <input type="hidden" name="signature" value="【シグネチャ】">
      <input type="hidden" name="Content-Type" value="${Content-Type}">
      <!-- Include any additional input fields here -->

      File to upload to S3: 
      <input name="file" type="file"> 
      <br> 
      <input type="submit" value="Upload File to S3"> 
    </form> 
  </body>
</html>

ポリシーとシグネチャの作り方(Java版)

public static void main(String[] args) throws Exception {

	String secretAccessKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

	// 有効期限
	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
	Calendar cal = Calendar.getInstance();
	cal.add(Calendar.DATE, 1);

	String policy_document = "{\"expiration\": \"" + sdf.format(cal.getTime()) + "\","
		+ "\"conditions\": ["
		+ "{\"bucket\": \"【バケット名】\"},"
		+ "[\"starts-with\", \"$key\", \"【パス(任意)】\"],"
		+ "{\"acl\": \"private\"},"
		+ "{\"success_action_redirect\": \"【リダイレクト先URL】\"},"
		+ "[\"starts-with\", \"$Content-Type\", \"\"],"
		+ "[\"content-length-range\", 0, 1048576]" + "]" + "}";

	String policy = (new BASE64Encoder()).encode(policy_document.getBytes("UTF-8")).replaceAll("\n", "").replaceAll("\r", "");

	// ポリシー
	System.out.println(policy);

	Mac hmac = Mac.getInstance("HmacSHA1");
	hmac.init(new SecretKeySpec(secretAccessKey.getBytes("UTF-8"), "HmacSHA1"));
	String signature = (new BASE64Encoder()).encode(hmac.doFinal(policy.getBytes("UTF-8"))).replaceAll("\n", "");

	// シグネチャ
	System.out.println(signature);
}

ポリシーにはformと同じ値を設定しないとアップロード時にエラーになるので注意

(おまけ)JavaのHTTPクライアントから利用してみる

public static void main(String[] args) throws ClientProtocolException,
		IOException, InvalidKeyException, NoSuchAlgorithmException {

	DefaultHttpClient httpclient = new DefaultHttpClient();
	String url = "https://【バケット名】.s3.amazonaws.com/";
	// アップロードするファイル
	File file = new File("src/main/resources/hoge.jpg");
	MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));

	String mime = "text/plain";
	Charset utf8 = Charset.forName("UTF-8");
	reqEntity.addPart("key",			new StringBody("【パス(任意)】${filename}", mime, utf8));
	reqEntity.addPart("AWSAccessKeyId",		new StringBody("【AWSアクセスキー】", mime, utf8));
	reqEntity.addPart("acl",			new StringBody("private", mime, utf8));
	reqEntity.addPart("success_action_redirect",	new StringBody("【リダイレクト先URL】", mime, utf8));
	reqEntity.addPart("policy",			new StringBody("【ポリシー】", mime, utf8));
	reqEntity.addPart("signature",			new StringBody("【シグネチャ】", mime, utf8));
	reqEntity.addPart("Content-Type",		new StringBody("${Content-Type}", mime, utf8));
	reqEntity.addPart("file",			new FileBody(file));

	// POSTリクエストを送信
	HttpPost request = new HttpPost(url);
	request.setEntity(reqEntity);
	HttpResponse response = httpclient.execute(request);
	HttpEntity resEntity = response.getEntity();

	// 結果を出力
	IOUtils.copy(resEntity.getContent(), System.out);
}

2012/09/11追記

有効期限はSimpleDateFormatを使用して文字列化していたがAWSのSDKには
「com.amazonaws.util.DateUtils」クラスが用意されているのでこちらを使うべきだと思われる

Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, 1);
String limit = new DateUtils().formatIso8601Date(cal.getTime());