ユーザにブラウザから直接S3にアップロードして欲しいことってあるよね〜
…と思ってググったところ、S3はpostでのアップロードを受け付けてくれるらしい。
Browser-Based Uploads Using POST
というわけでやってみよう。
今回は、UploadifyというFlash製のアップローダを利用した。
1.事前準備
はじめに、ブラウザからS3に何をPOSTするのかを確認しておこう。
まず、formのpost先から。
action="http://[bucket].s3.amazonaws.com/"
次にpostする内容。
主に使いそうなものは以下で、ほかにもこのページに全要素の一覧がある。
postする主な要素 | 必須 | 補足 |
---|---|---|
AWSAccessKeyId | ○ | |
acl | アクセス権限。private, public-readなど。 | |
key | ○ | 保存先のフルパス。${filename}を指定するとアップロード元のオリジナルのファイル名が使われる。 |
policy | ポリシードキュメント。JSONで書いて、Base64でエンコードする。匿名で書き込み可能なbucketはpolicyなしでもOK。 | |
signature | ポリシードキュメントに対するsignature。HMAC SHA-1を使って、policyをSecret Keyでsignする。こちらもBase64でエンコード。 | |
success_action_redirect | アップロード後のリダイレクト先URL。 | |
success_action_status | アップロード後にリダイレクトしない場合の、S3からのレスポンスのフォーマットを指定する。Flashの場合は201を推奨とのこと。(Flashはバージョンによっては空bodyのレスポンスを正しく処理できない) |
2.crossdomain.xmlの配置
今回利用するUploadifyはFlash製だが、Flashはセキュリティポリシー上、swfから自身の設置されたドメインの外にはアクセスできないようになっている。これを回避するためにcrossdomain.xmlを配置しよう。参考リンク
対象のbucketのルートにcrossdomain.xmlを配置
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*" secure="false" />
</cross-domain-policy>
crossdomain.xmlを「パブリックに公開」
crossdomain.xmlがswfから見える必要があるので公開設定にしておく。
3.policyとsignatureを作る
policyとsignatureを作って渡すことで、S3側での認証を行う。
匿名で書き込み可能なbucketの場合はこれらは不要らしいが、さすがに誰でもアップロードできてしまうのはまずそうなので作ることにする。
policyは都度生成し、毎回24時間後に有効期限が切れるように設定、とかが良いのかなあ。良い感じの運用方法をご存知の方は教えてもらえると嬉しいです。
policy
policyには、有効期限(expiration)と条件(conditions)が含まれる。
以下のようにJSONで記述し、base64エンコードして渡す。
{ "expiration": "2013-03-15T09:00:00.000Z",
"conditions": [
{"acl": "authenticated-read"},
{"bucket": "bucket-name"},
["success_action_status' => "201"],
["starts-with", "$key", "uploads"],
["starts-with", "$filename", ""]
]
}
※上記の例では次のような条件を指定している
- 2013年3月15日9時(GMT)までにアップロードしなければならない
- アクセス権限は authenticated-read でなければならない
- アップロード先は bucket-name でなければならない
- success_action_status は 201 でなければならない
- keyは uploads で始まらなければならない
- ファイル名はなんでもよい
PHPでpolicyを作ると次のような感じ。
$policy_json = json_encode(array(
'expiration' => date('Y-m-d\TH:i:s.000\Z', time() + (60 * 60 * 24) - (60 * 60 * 9)),
'conditions' => array(
array('acl' => 'authenticated-read'),
array('bucket' => 'bucket-name'),
array('success_action_status' => '201'),
array('starts-with', '$key', 'uploads'),
array('starts-with', '$filename', '')
)
));
$policy = base64_encode($policy_json);
signature
signatureはpolicyとセットで渡すもの。HMAC SHA-1を使ってpolicyをAWSのSecret Keyでsignする。
$signature = base64_encode(hash_hmac('sha1', $policy, AWS_SECRET_KEY, true));
4.Uploadifyの設定
あとはボタンを配置してUploadifyに必要な値を渡したらおしまい。
jsとcssを読み込んでボタンを配置
こちらは…ご覧の通りである。
…
<head>
…
<link rel="stylesheet" type="text/css" href="uploadify.css" />
<script type="text/javascript" src="jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="jquery.uploadify-3.1.min.js"></script>
…
<body>
…
<input type="file" name="file_upload" id="file_upload" />
…
Uploadifyの設定
こちらもほぼご覧の通りである。
ただ、2箇所ほどはまったところがある。
- fileObjNameの指定が必須
- これを指定しないとPOSTサイズが大きすぎてS3に怒られる
- 値はなんでもよい
- aclやsuccess_action_statusなどはすべて事前にpolicyで定義しておく必要がある
- でないとS3に怒られる
$(function() {
$('#file_upload').uploadify({
'swf' : 'uploadify.swf',
'uploader' : 'https://bucket-name.s3.amazonaws.com/',
'fileObjName' : 'file',
'formData' : {
'AWSAccessKeyId' : '[Access Keyを指定]',
'acl' : 'authenticated-read',
'key' : '[ファイルのパスを指定(ファイル名含む)]',
'policy' : '<?= $policy ?>',
'signature' : '<?= $signature ?>',
'success_action_status' : '201'
}
});
});
これで完了!
あとはS3にアップロードされたファイルを煮るなり焼くなりやっちゃって下さい。