ブラウザからS3に直接ファイルをアップロードする(リダイレクト)

先日の続きでリダイレクトしたときにリダイレクト先に渡される情報をチェックしました。

変数「request」はjavax.servlet.http.HttpServletRequestを使用

<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>servlet-api</artifactId>
	<version>2.5</version>
</dependency>
サーバ側のソース
System.out.println("[Method]\t" + request.getMethod());
System.out.println("[Params]");
Enumeration paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()){
	String name = paramNames.nextElement().toString();
	System.out.println("\t" + name + "\t" + Arrays.toString(request.getParameterValues(name)));
}
System.out.println("[Query]\t" + request.getQueryString());
出力結果
[Method]        GET
[Params]
        etag    ["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
        bucket  [【バケット名】]
        key     [【バケットからのパス】【ファイル名】]
[Query]   bucket=【バケット名】&key=【バケットからのパス】【ファイル名】&etag=%22xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%22

バケット名やファイルまでのパスはもらえるみたい

リダイレクト先のRESTサービス実装

以上のことを踏まえてJerseyを使ってリダイレクト先のRESTサービスを作成すると以下のようになる
PathParamは適当に作成したのでこれは必要に応じて変更する

@Path("storage")
public class Storage {

	@GET
	@Produces( { MediaType.APPLICATION_JSON })
	@Path("/{domain}/{id}/{filename}")
	public Response redirect(@PathParam("domain") String domain,
			@PathParam("id") String id, @PathParam("filename") String filename,
			@QueryParam("etag") String etag,
			@QueryParam("bucket") String bucket, @QueryParam("key") String key) {

		System.out.println("[DOMAIN]\t" + domain);
		System.out.println("[  ID  ]\t" + id);
		System.out.println("[ NAME ]\t" + filename);
		System.out.println("[ ETAG ]\t" + etag);
		System.out.println("[BUCKET]\t" + bucket);
		System.out.println("[  KEY ]\t" + key);

		// TODO DB登録処理など

		Response.ResponseBuilder responseBuilder = Response.ok();
		// TODO クライアントに返す値をセット
		return responseBuilder.build();
	}
}
クライアント実装例
// 有効期限
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, 1);
String limit = new DateUtils().formatIso8601Date(cal.getTime());
String bucket = "【バケット名】";
String path = "【バケットからのパス】";
String key = "【S3上のファイル名】";

DefaultHttpClient httpclient = new DefaultHttpClient();

String url = "https://【バケット名】.s3.amazonaws.com/";
// アップロードするファイル
File file = new File("src/main/resources/input/hoge.csv");
// ファイル名(リダイレクトURLに日本語ファイル名をそのまま使用すると失敗するのでエンコード)
String filename = URLEncoder.encode(file.getName(), "UTF-8");

// リダイレクト先URL
String redirectURL = "https://【ホスト名】/【Jersey】/storage/" + 【ドメイン】 + "/" + 【ID】 + "/" + filename;

// ポリシー
String policy_document = "{\"expiration\": \"" + limit + "\","
	+ "\"conditions\": ["
	+ "{\"bucket\": \"" + bucket + "\"},"
	+ "[\"starts-with\", \"$key\", \"" + path + "\"],"
	+ "{\"acl\": \"private\"},"
	+ "{\"success_action_redirect\": \"" + redirectURL + "\"},"
	+ "[\"starts-with\", \"$Content-Type\", \"\"],"
	+ "[\"content-length-range\", 0, 1048576]" + "]" + "}";
String policy = (new BASE64Encoder()).encode(
		policy_document.getBytes("UTF-8")).replaceAll("\n", "")
		.replaceAll("\r", "");

// シグネチャ
Mac hmac = Mac.getInstance("HmacSHA1");
hmac.init(new SecretKeySpec("【AWSシークレットキー】".getBytes("UTF-8"), "HmacSHA1"));
String signature = (new BASE64Encoder()).encode(
		hmac.doFinal(policy.getBytes("UTF-8"))).replaceAll("\n", "");

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(path + key, 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(redirectURL, mime, utf8));
reqEntity.addPart("policy",			new StringBody(policy, mime, utf8));
reqEntity.addPart("signature",			new StringBody(signature, mime, utf8));
reqEntity.addPart("Content-Type",		new StringBody("${Content-Type}", mime, utf8));
reqEntity.addPart("file",			new FileBody(file));

HttpPost request = new HttpPost(url);
request.setEntity(reqEntity);
HttpResponse response = httpclient.execute(request);
HttpEntity entity = response.getEntity();

IOUtils.copy(entity.getContent(), System.out);
出力
[DOMAIN]        【ドメイン】
[  ID  ]        【ID】
[ NAME ]        hoge.csv
[ ETAG ]        "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
[BUCKET]        【バケット名】
[  KEY ]        【バケットからのパス】【S3上のファイル名】

以上。
etagは何に使うか分からないけどデフォルトでは「"」で囲まれてるので注意