HTML アップロードフォームから S3 へのファイルアップロード

S3 というのは Amazon の Simple Storage Service で、スケーラブルなクラウドストレージサービスです。

ここでは HTML のフォームから S3 へファイルをアップロードする方法を示します。

ポイントになるのは、アクセスキーの設定およびポリシーの設定などになります。

HTML フォームから S3 へのファイルアップロード

HTML のファイルアップロードフォームはこのような形になります。

<form action="http://XXXXX.s3.amazonaws.com/"
	method="post"
	enctype="multipart/form-data">
<input type="hidden" name="key" value="uploads/myfilename.jpg">
<input type="hidden" name="AWSAccessKeyId"
	value="あなたの AWS アクセスキー">
<input type="hidden" name="acl" value="public-read">
<input type="hidden" name="success_action_redirect"
	value="http://example.com/upload_ok.html">
<input type="hidden" name="policy"
	value="ポリシーファイルの Base 64 (後述)">
<input type="hidden" name="signature" value="シグネチャ">
<input type="hidden" name="Content-Type" value="image/jpeg">

<input name="file" type="file">
<input type="submit" value="S3 へのアップロード">
</form>

形式は普通に一般的なものです (詳細は「HTML フォーム・ファイルアップロードの仕組み」を参考にしてください。)

よくみると、hidden の input として、key、AWSAccessKeyId、acl、success_action_redirect、policy、signature、Content-Type などの値が設定されています。

key というのは、アップロード先の S3 ストレージでのファイル名にあたるものです。ここでは、uploads/myfilename.jpg としていますが、これによって、 uploads というフォルダ内の myfilename.jpg というキー名でアップロードされたファイルが保存されることになります。

AWSAccessKeyId は AWS の管理画面でとれるあなたのアクセスキーを設定します。

acl はアップロード後のファイルのアクセス権を設定します。ここでは public-read にしていますから、 アップロード後、直ちに誰でもそのファイルが閲覧可能になります。設定しなければプライベートになります。

success_action_redirect はファイルアップロードが成功したあとに、リダイレクトされる先です。

policy と signature は後ほど説明します。

Content-Type はアップロードするファイルの MIME タイプをセットします。

S3 へのアップロード時のポリシーとシグネチャ設定

さて、設定しなければならないパラメータとしてポリシーとシグネチャについて説明します。

ポリシーについては、例えば次のような形式で有効期限、バケツ、キー名のルール (starts-with)、acl、success_action_redirect、 Content-Type のルールを設定します。

{ "expiration": "2020-01-01T12:00:00.000Z",
  "conditions": [
    {"bucket": "mybucket"},
    ["starts-with", "$key", "uploads/"],
    {"acl": "public-read"},
    {"success_action_redirect": "http://example.com/upload-ok.html"},
    ["starts-with", "$Content-Type", "image/"]
  ]
}

ポリシーについては、これを Base64 エンコードして policy 値として設定します。

シグネチャについては、エンコードされたポリシーに対して AWS シークレットキーをパスワードとして、HMAC-SHA-1 を設定します。

参考までにこれを計算する Java プログラムは次のようになります。

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import sun.misc.BASE64Encoder;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;


public class sigapp1 {

  public static void main(String[] args) {

    final String aws_secret_key = "あなたの AWS シークレットキー";

    try {
            
      String policy_document = getFileContents("policy.txt");
      
      String policy = (
        new BASE64Encoder()).encode(
          policy_document.getBytes("UTF-8")).replaceAll(
            "\n","").replaceAll(
              "\r","");
  
      System.out.println("Policy:");
      System.out.println(policy);
      System.out.println("");
      
      Mac hmac;
      hmac = Mac.getInstance("HmacSHA1");
      hmac.init(new SecretKeySpec(aws_secret_key.getBytes(
        "UTF-8"), "HmacSHA1"));
      String signature = (new BASE64Encoder()).encode(hmac.doFinal(
        policy.getBytes("UTF-8"))).replaceAll(
        "\n", "");

      System.out.println("Signature:");
      System.out.println(signature);
      System.out.println("");
      
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    } catch (InvalidKeyException e) {
      e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }

  }
  
  protected static String getFileContents(String path) 
  	throws IOException {
    StringBuffer sb = new StringBuffer();
    BufferedReader buffReader = new BufferedReader(
    	new FileReader(path));
    String line;
          
    while( (line = buffReader.readLine()) != null ){
      sb.append(line);
    }

    buffReader.close();
    return sb.toString();
  }

}

このプログラムで出力された値を policy および signature にセットすれば OK です。