Movatterモバイル変換


[0]ホーム

URL:


LoginSignup
49

Go to list of users who liked

53

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Railsで(大きな)画像をS3に直接アップロード

Posted at

Heroku Dev Center「Direct to S3 Image Uploads in Rails」の覚え書きです。

環境

  • Rails 4.1.8
  • heroku-toolbelt 3.16.0
  • aws-sdk 1.59.0
  • JQuery UI (1.11.2)
  • jQuery File Upload Plugin (9.8.0)

サンプル

参照

覚え書き

Philosophy

この記事ではjQuery-File-Uploadプラグインと AWS gem を用います。
画像をS3に直接アップロードできるCarrierWaveDirect のような他のライブラリもありますが、クライアント側に関する低レベルの知識なしでのそれらの実装は困難です。

jQuery-File-Upload プラグインを用いると、JavaScript のコードは比較的読みやすく短いものになり、そのコードを任意の画像アップロード入力フォームとして再利用することもできます。
また、ユーザインタフェースは非常にカスタマイズ可能です。
Rails側では、AWSで事前に署名したPOSTを生成し、画像のURLをデータベースに保存します。

Example app

このアプリではUser モデルを持ち、アバター画像をユーザー単位で S3 に保存したいと思います。

rails new direct-s3-examplecddirect-s3-examplerails generate scaffold user name avatar_urlrake db:migrate

S3

ファイルを S3 に送る前に、S3 のアカウントと、適切に構成されたバケットが必要になります。

S3 SDK

次に Ruby で S3 とやりとりするためのライブラリが必要になります。
aws-sdk を Gemfile に追加してbundle install してください。

Gemfile
gem'aws-sdk'

ローカルの開発では、.env ファイルとForeman を使います。
次のように環境変数を追加してください。
尚、下記の値は例ですので、S3 で設定/取得したものを書いてください。

.env
S3_BUCKET=my-s3-developmentAWS_ACCESS_KEY_ID=EXAMPLEKVFOOOWWPYAAWS_SECRET_ACCESS_KEY=exampleBARZHS3sRew8xw5hiGLfroD/b21p2l

確認

$foreman run rails runner"puts ENV['S3_BUCKET']"my-s3-development

環境変数を設定できたら、コントローラーから使えるように S3 オブジェクトを用意します。

config/initializers/aws.rb
AWS.config(access_key_id:ENV['AWS_ACCESS_KEY_ID'],secret_access_key:ENV['AWS_SECRET_ACCESS_KEY'])S3_BUCKET=AWS::S3.new.buckets[ENV['S3_BUCKET']]

Cross origin support

(参考)CORS(Cross-Origin Resource Sharing)について整理してみた

環境に合わせてバケットの CORS 設定を変更します。
適切なオリジンをAllowedOrigin に設定しましょう。

<?xml version="1.0" encoding="UTF-8"?><CORSConfigurationxmlns="http://s3.amazonaws.com/doc/2006-03-01/"><CORSRule><AllowedOrigin>*</AllowedOrigin><AllowedMethod>GET</AllowedMethod><AllowedMethod>POST</AllowedMethod><AllowedMethod>PUT</AllowedMethod><AllowedHeader>*</AllowedHeader></CORSRule></CORSConfiguration>

Pre-signed post

AWS ruby gem で事前に署名した(Pre-signed)POSTを生成します。

あなたのユーザーや顧客が特定のオブジェクトをあなたのバケットにアップロードしたい場合に、事前に署名したURLは役立ちます。詳しくはClass: AWS::S3::PresignedPost をご覧ください。

users_controller.rb に pre-signed post を生成する行を追加します。

app/controllers/users_controller.rb
# GET /users/newdefnew@s3_direct_post=S3_BUCKET.presigned_post(key:"uploads/#{SecureRandom.uuid}/${filename}",success_action_status:201,acl: :public_read)@user=User.newend

Client side code

一時的なキャッシュとしてサーバーに頼ることはできないので、S3 にファイルを配信するために、クライアント側のコード(JavaScript)を使用しなければなりません。

HTML 5はファイルAPIを導入していますが、IE 10までサポートされませんでした。これを回避するために、jQuery File Upload を使います。これを使うには先に JQuery UI が必要になります。

  • JQuery UI
  • jQuery File Upload

Rails プロジェクトに JavaScript ファイルを取り込みます。

$curl\https://raw.githubusercontent.com/jquery/jquery-ui/master/ui/widget.js\>> app/assets/javascripts/jquery.ui.widget.js$curl\https://raw.githubusercontent.com/blueimp/jQuery-File-Upload/master/js/jquery.fileupload.js\>> app/assets/javascripts/z.jquery.fileupload.js
app/assets/javascripts/application.js
..//= require jquery.ui.widget.js//= require z.jquery.fileupload//= require_tree .

確認

rails sopen http://localhost:3000/users/new
JavaScriptコンソール
>console.log($().fileupload)function(options){varisMethodCall=typeofoptions==="string",args=slice.call(arguments,1),returnValue=this;//...

Prepare the view

  • form_for に directUpload クラスを追加
  • avatar_url を f.file_field に変更
app/views/users/_form.html.erb
<%=form_for(@user,html:{class:"directUpload"})do|f|%><%if@user.errors.any?%>..<divclass="field"><%=f.label:avatar_url%><br><%=f.file_field:avatar_url%></div>

Detecting file field on the client side

以下のものが用意できました。

  • S3 バケット
  • 有効な pre-signed post オブジェクト
  • User モデル(avatar_url、ファイル入力)

ユーザーの画像を S3 から取得したり、avatar_url に URL を保存する必要がありますが、これは主に JavaScript での手動処理になります。

・・・省略・・・

プログレスバーのスタイルを指定します。

app/assets/stylesheets/screen.css
.progress{max-width:600px;margin:0.2em00.2em0;}.progress.bar{height:1.2em;padding:0.2em;color:white;display:none;}

Finished jquery-file-upload code

・・・省略・・・

jQuery-File-Upload callbacks

タグ<script></script> を追加します。

app/views/users/new.html.erb
<h1>New user</h1><%=render'form'%><%=link_to'Back',users_path%><script>$(function(){$('.directUpload').find("input:file").each(function(i,elem){varfileInput=$(elem);varform=$(fileInput.parents('form:first'));varsubmitButton=form.find('input[type="submit"]');varprogressBar=$("<div class='bar'></div>");varbarContainer=$("<div class='progress'></div>").append(progressBar);fileInput.after(barContainer);fileInput.fileupload({fileInput:fileInput,url:'<%= @s3_direct_post.url %>',type:'POST',autoUpload:true,formData:<%=@s3_direct_post.fields.to_json.html_safe%>,paramName:'file',dataType:'XML',replaceFileInput:false,progressall:function(e,data){varprogress=parseInt(data.loaded/data.total*100,10);progressBar.css('width',progress+'%')},start:function(e){submitButton.prop('disabled',true);progressBar.css('background','green').css('display','block').css('width','0%').text("Loading...");},done:function(e,data){submitButton.prop('disabled',false);progressBar.text("Uploading done");// extract key and generate URL from responsevarkey=$(data.jqXHR.responseXML).find("Key").text();varurl='//<%= @s3_direct_post.url.host %>/'+key;// create hidden fieldvarinput=$("<input />",{type:'hidden',name:fileInput.attr('name'),value:url})form.append(input);},fail:function(e,data){submitButton.prop('disabled',false);progressBar.css("background","red").text("Failed");}});});});</script>

備考

# .envの環境変数を参照するためforemanで起動foreman run rails server
# 確認aws--profile foo s3lss3://aaa-bbb-ccc-1/uploads/# 全て削除(注意!)aws--profile bar s3rms3://aaa-bbb-ccc-1/uploads/--recursive
49

Go to list of users who liked

53
0

Go to list of comments

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49

Go to list of users who liked

53

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?


[8]ページ先頭

©2009-2025 Movatter.jp