1. komakomako

    Posted

    komakomako
Changes in title
+【Rails】ブラウザからS3へ画像を直接アップロード
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,205 @@
+[こちら](http://qiita.com/yuku_t/items/40b7daf018d3dab48974)を参考に, 画像をブラウザからS3へ直接アップロードできるようにしました.
+
+# 1. AWSの準備 (時間:5分)
+
+IAMでS3アクセス用のユーザを作成します. まずは以下のようにコンソール画面からIAMへアクセス.
+
+<img width="500" alt="ss 2017-09-18 12.44.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/80357/7ffd7963-918d-d7ee-e328-7fd87975ca27.png">
+
+次に, 以下のUserをクリック
+<img width="500" alt="ss 2017-09-18 12.48.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/80357/7d7bbddb-7fea-651a-ab6a-a60c39b0e40e.png">
+
+Add userをクリック
+<img width="500" alt="ss 2017-09-18 12.48.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/80357/c7520ae6-0315-5333-b687-77fa65d2e3fb.png">
+
+任意の名前を入力. プログラマティックアクセスにチェックして次へ.
+<img width="500" alt="ss 2017-09-18 12.48.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/80357/9ba6d82b-4065-9a85-5b63-18944c2d5548.png">
+
+既存ポリシーの中から AmazonS3FullAccessを見つけてチェック.
+<img width="500" alt="eee" src="https://qiita-image-store.s3.amazonaws.com/0/80357/86d326f3-9174-621b-3c34-7535e3dd7a5b.png">
+
+以上でユーザ作成は完了. 詳細画面でCreate Access keyをクリックして必要なキーを取得する.
+<img width="500" alt="aaa" src="https://qiita-image-store.s3.amazonaws.com/0/80357/aca0cdc0-0c55-794c-0812-afe4b63bb43c.png">
+
+このようにkeyIDとシークレットkeyが表示されるので, メモっておく.
+<img width="500" alt="ss 2017-09-18 12.50.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/80357/030f4b36-ef35-7119-06f9-a83d673c3622.png">
+
+次にS3の設定を行う.
+
+適当なバケットを作成する. 各種設定はデフォルトのままで良い. 作成し終えたら, そのバケットのプロパティを開き, Permissions -> CORS configurationに以下のように記述する. これで終わり.
+
+<img width="500" alt="ssd" src="https://qiita-image-store.s3.amazonaws.com/0/80357/6c4bd597-4055-1418-0828-d94b00dc9b49.png">
+
+# 2. バックエンドでS3ポリシーを作成
+
+自身のRailsアプリに, POSTメソッドで叩ける適当なエンドポイントを作成する.
+
+`routes.rb`に例えば以下のように記述.
+
+```ruby
+post 'image_upload', to: 'images#upload'
+```
+
+そして, `images#upload`を次のように定義する.
+
+```ruby
+class ImagesController < ApplicationController
+ # Deviceを使って認証処理をしている場合, 次の1行によって認証を要求できる.
+ before_action :authenticate_user!, only: [:upload]
+
+ # 先ほど作ったバケットの名前とアクセスKeyIdとシークレットkey
+ S3_BUCKET = 'your-bucket-name'
+ AWS_ACCESS_KEY_ID = 'XXX'
+ AWS_SECRET_KEY = 'XXX'
+
+ def upload
+ # アップロード後のファイル名
+ key = 'hogehoge'
+
+ acl = 'public-read'
+ ctype = params[:content_type]
+
+ # ポリシー作成
+ policy_document = {
+ # 1分間のみ有効
+ expiration: (Time.now + 1.minute).utc,
+ conditions: [
+ # アップロード先のS3バケット
+ { bucket: S3_BUCKET },
+ # ファイルの権限
+ { acl: acl },
+ # ファイル名
+ { key: key },
+ # ファイルの形式
+ { 'Content-Type' => ctype },
+ # アップロード可能なファイルのサイズ
+ ['content-length-range', params[:size], params[:size]]
+ ]
+ }.to_json
+ policy = Base64.encode64(policy_document).gsub("\n", '')
+
+ # signatureの作成
+ signature = Base64.encode64(
+ OpenSSL::HMAC.digest(
+ OpenSSL::Digest::Digest.new('sha1'),
+ AWS_SECRET_KEY, policy)).gsub('\n', '')
+
+ # アップロードに必要な情報をJSON形式でクライアントに返す
+ render json: {
+ url: "https://#{S3_BUCKET}.s3.amazonaws.com/",
+ form: {
+ AWSAccessKeyId: AWS_ACCESS_KEY_ID,
+ signature: signature,
+ policy: policy,
+ key: key,
+ acl: acl,
+ 'Content-Type' => ctype
+ }
+ }
+ end
+end
+```
+
+# 3. フロントエンドでポリシー取得しアップロード実行
+
+画像をアップロードしたいViewに, 以下のようなjavascriptを仕込みます.
+
+```html
+
+画像を投稿します
+
+<div style="width: 500px">
+ <form enctype="multipart/form-data" method="post">
+ <input type="file" name="userfile" accept="image/*">
+ </form>
+</div>
+
+<div id="thumbnail" style="max-width: 100px;">
+ <img src="/plus.png" id="image_to_upload">
+</div>
+
+<button class="btn btn-primary" id="upload">投稿</button>
+
+<script type="text/javascript">
+$(function() {
+ var file = null;
+
+ // アップロードするファイルを選択
+ $('input[type=file]').change(function() {
+ file = $(this).prop('files')[0];
+ // 画像以外は処理を停止させるためファイル形式をチェック
+ if (file.type != 'image/jpeg' && file.type != 'image/png') {
+ // 画像でない場合は消す
+ var img_src = $('<img>').attr('src', '/plus.png')
+ $('#thumbnail').html(img_src);
+ file = null
+ return;
+ }
+
+ // サムネ表示
+ var reader = new FileReader();
+ reader.onload = function() {
+ var img_src = $('<img>').attr('src', reader.result)
+ img_src.css('width', '500px');
+ $('#thumbnail').html(img_src);
+ }
+ reader.readAsDataURL(file);
+ });
+
+ // アップロードボタンクリック
+ $('#upload').click(function(){
+ // ファイルが指定されていなければ何も起こらない
+ if(!file) {
+ return;
+ }
+
+ // ポリシーを発行する
+ $.ajax({
+ url: 'http://localhost:3000/image_upload',
+ type: 'POST',
+ data: {
+ content_type: file.type,
+ size: file.size
+ }
+ })
+ .done(function( data, textStatus, jqXHR ) {
+ // 取得したポリシーをフォームデータの形に整形する
+ var name, fd = new FormData();
+ for (name in data.form) if (data.form.hasOwnProperty(name)) {
+ fd.append(name, data.form[name]);
+ }
+ fd.append('file', file); // ファイルを添付
+
+ $.ajax({
+ url: data.url,
+ type: 'POST',
+ dataType: 'json',
+ data: fd,
+ processData: false,
+ contentType: false
+ })
+ .done(function( data, textStatus, jqXHR ) {
+ console.log('success!')
+ })
+ .fail(function( jqXHR, textStatus, errorThrown ) {
+ // アップロード時のエラー
+ console.log('error: 2');
+ });
+
+ })
+ .fail(function( jqXHR, textStatus, errorThrown ) {
+ // ポリシー取得時のエラー
+ console.log('error: 1');
+ });
+
+ });
+
+});
+</script>
+```
+
+次のようなUIが出来上がります. 「ファイルを選択」をクリックしてファイルを適当に選択すると, サムネイルが表示されます. この状態で投稿ボタンを押すと画像がS3へ直接アップロードされます.
+<img width="500" alt="ss 2017-09-18 13.16.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/80357/d6e23d56-8573-41df-1737-ea62e778f606.png">
+
+
+以上です.