[AWS] ブラウザからダイレクトで S3 に画像をアップロードする

AWS

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

S3 にブラウザから直接ファイルをアップロードする方法について試してみました。
S3 には直接 POST でアップロード可能なので、HTML の Form を使ってアップロードを行います。
この際に、PolicySignature というものが必要で、これらをアップロードしたファイルと同時に S3 に渡すことで、認証を行う仕組みになっています。

事前準備

  • S3 バケットの作成
  • 自身の AWS のアクセスキーとシークレットキーを手元に用意

Policy と Signature の作成

最初に Policy Document を作成し、S3 へダイレクトアップロードする際の制約を定めます。
Policy Document は以下のような形式です。

{"expiration": "2013-08-17T00:00:00Z",
  "conditions": [ 
    {"bucket": "bucket-name"}, 
    ["starts-with", "$key", "uploads/"],
    {"acl": "private"},
    {"success_action_redirect": "http://localhost/success.html"},
    ["starts-with", "$Content-Type", ""],
    ["content-length-range", 0, 1048576]
  ]
}

JSON 形式で、バケット名、有効期限やアップロードのファイル最小、最大サイズ等を指定します。
key には uploads/ と指定しているので、対象の S3 のバケット内にマネージメントコンソールから uploads という名前で Create Folder しておきます。
acl は、アップロードするファイルのアクセス権限を指定します。以下の形式が指定可能です。
private | public-read | public-read-write | authenticated-read | bucket-owner-read | bucket-owner-full-control
success_action_redirect には、アップロードが成功したあとのリダイレクト先を指定します。

上記の Policy Document を HTML の Form 上では、Base64 エンコードしたものを指定します。
更にこれにAWSシークレットキーを使って、Signature を作成します。
その作成コードが以下です。Ruby で作成しています。

require 'base64'
require 'openssl'
require 'digest/sha1'

policy_document = <<EOS
{"expiration": "2013-08-17T00:00:00Z",
  "conditions": [ 
    {"bucket": "bucket-name"}, 
    ["starts-with", "$key", "uploads/"],
    {"acl": "private"},
    {"success_action_redirect": "http://localhost:3000/success.html"},
    ["starts-with", "$Content-Type", ""],
    ["content-length-range", 0, 1048576]
  ]
}
EOS

policy = Base64.encode64(policy_document).gsub("\n","")
puts "policy: #{policy}"

aws_secret_key = YOUR_AWS_SECRET_KEY
signature = Base64.encode64(
    OpenSSL::HMAC.digest(
        OpenSSL::Digest::Digest.new('sha1'), 
        aws_secret_key, policy)
    ).gsub("\n","")

puts "signature: #{signature}"

aws_secret_key に自分の AWSシークレットキーを指定してください
この Ruby コードをコマンドラインから実行します。

$ ruby generate_signature.rb
policy: eyJleHBpcm......
signature: ngYf/QScF4......

出力された policysignature を後述の HTML のテンプレートに指定します。

HTML のテンプレート

以下が S3 に POST する HTML のテンプレートです。

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

  <body>
    <form action="https://bucket-name.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
      <input type="hidden" name="key" value="uploads/${filename}">
      <input type="hidden" name="AWSAccessKeyId" value="YOUR_AWS_ACCESS_KEY">
      <input type="hidden" name="acl" value="private">
      <input type="hidden" name="success_action_redirect" value="http://localhost/success.html">
      <input type="hidden" name="policy" value="YOUR_POLICY_DOCUMENT_BASE64_ENCODED">
      <input type="hidden" name="signature" value="YOUR_CALCULATED_SIGNATURE">
      <input type="hidden" name="Content-Type" value="image/jpeg">

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

action の URL の bucket-name には自分のバケット名を指定してください。https を指定していますが、http でも可能です。
AWSAccessKeyId は、AWSのアクセスキーを指定してください。
key は、アップロードするファイル名を指定します。${filename} とすることで、選択したファイル名のまま S3 にアップロードされます。
aclsuccess_action_redirect は、前述の Policy Document の内容と合わせる必要があります。
policysignature は上記で作成したものを指定します。

S3 に直接アップロード

HTMLの準備ができたら Apache のドキュメントルート等に置き、アクセスします。

130816-0001

ファイルを指定して、アップロード中…

130816-0003-2

アップロードが終わりました!

130816-0002

マネージメントコンソールから S3 を確認すると、アップロードされていますね!

130816-0004-2

まとめ

S3 の Post サポートは便利ですね。ファイルサイズや有効期限を上手に Policy で定めて活用すれば特定の人に限定して公開したりもできますし、 直接 S3 なので、サーバーサイドの負荷も心配する必要はないですね。