Rails での S3 への画像の直接アップロード
最終更新日 2020年06月23日(火)
Table of Contents
この記事の内容はライブラリの最新の変更を反映していないため、現在は通用しない可能性があります。
この記事では、S3 への直接アップロードを Rails アプリに追加する方法を示します。 Ruby および Rails 向けの S3 画像アップロードソリューションとして一般的なものは Paperclip や CarrierWave など多数ありますが、これらのソリューションではサーバーを一時キャッシュとして使用します。
これらのソリューションでは通常、ファイルを Heroku にアップロードしてから S3 にストリーミングします。ファイルのサイズが小さければこの方法で問題ありませんが、Heroku の一時的なファイルシステムの性質上、S3 にアップロードする前に大きなファイルが dyno から削除されてしまう場合があります。
より確実性のある別の方法は、クライアント側から S3 に画像を直接アップロードし、画像が完全にアップロードされたら参照 URL をデータベースに保存するというものです。この方法では、画像のアップロード中に dyno が再起動されても問題なく、画像アップロードの追加負荷を dyno が処理する必要がなく、ファイルが 2 回 (dyno と S3 に 1 回ずつ) アップロードされるのを待つ必要もありません。
唯一の短所は、すべてのロジックをクライアント側で実行する必要があることです。 この記事では、これを行う方法を示します。
原理
この記事では、jQuery-File-Upload プラグインと AWS gem を使用します。carrier wave direct など、S3 に画像を直接アップロードするために使用できるライブラリは他にもありますが、そのようなライブラリの実装は、クライアント側のすべての考慮事項に関する低レベルの知識がなければ難しい場合があります。
jQuery-File-Upload プラグインを使用して、読みやすくて短い JavaScript コードを作成します。このコードは、画像アップロード入力を使用するフォームであればどのフォームでも再利用できます。UI の動作は細かくカスタマイズ可能であり、ユーザーの視点からも動作は非常にシンプルです。Rails 側で AWS presigned-post を作成し、画像 URL をデータベースに保存します。
アプリの例
この例の目的のために、User
モデルがあり、各ユーザーのアバターを S3 に保存すると仮定します。UsersController
があることも前提です。まだプロジェクトがない場合でも、最初から手順を進めることができます。
$ rails new direct-s3-example
$ cd direct-s3-example
$ rails generate scaffold user name avatar_url
invoke active_record
create db/migrate/20140519195131_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
# ...
まず、データベースを移行できます。
$ rake db:migrate
次に、任意のエディタでプロジェクトを開きます。ここで、アプリケーションが S3 と通信できるようにする必要があります。
S3
ファイルを S3 に送信する前に、S3 アカウントを取得し、バケットを正しく設定しておく必要があります。
指示に従って、次の手順に進む前に S3 アカウントを取得しておいてください。
Rails アプリと S3 アカウントの用意ができたら、クライアント側ではファイルを操作する必要があり、Ruby 側では S3 と対話するためのライブラリが必要になります。
S3 SDK
S3 との対話には Amazon Ruby SDK を使用します。アプリケーションの Gemfile に次の内容を追加します。
gem 'aws-sdk', '~> 2'
次に、bundle install
を実行します。ローカル開発のために、.env
ファイルおよび heroku local を使用していると仮定します。.env
ファイルを開き、S3_BUCKET
、AWS_ACCESS_KEY_ID
、および AWS_SECRET_ACCESS_KEY
が、S3 バケットを作成したときの値に設定されていることを確認します。
$ cat .env
S3_BUCKET=my-s3-development
AWS_ACCESS_KEY_ID=EXAMPLEKVFOOOWWPYA
AWS_SECRET_ACCESS_KEY=exampleBARZHS3sRew8xw5hiGLfroD/b21p2l
値が環境に書き込まれていることを確認します。そのためには、このコマンドを実行して、それが .env
内の値と一致していることを確認します。
$ heroku local:run rails runner "puts ENV['S3_BUCKET']"
my-s3-development
ローカルで環境変数を設定したら、コントローラで使用する新しい S3 オブジェクトをインスタンス化する必要があります。config/initializers/aws.rb
でイニシャライザを作成します。次に、AWS を設定し、グローバル S3 定数を作成します。
Aws.config.update({
region: 'us-east-1',
credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY']),
})
S3_BUCKET = Aws::S3::Resource.new.bucket(ENV['S3_BUCKET'])
Heroku で実行している本番環境用に、新しい S3 バケットの作成プロセスを繰り返す必要があります。これらの値は heroku config:set
を使用して設定する必要があります。たとえば、本番環境バケットの名前が my-s3-production
の場合、次を実行することによって Heroku で適切な値を設定できます。
$ heroku config:set S3_BUCKET=my-s3-production
本番環境の資格情報を使用して、AWS_ACCESS_KEY_ID
および AWS_SECRET_ACCESS_KEY
に対してこのプロセスを繰り返します。
オリジン間サポート
ブラウザのデフォルト設定では、現在描画中のページの外部にある他のサービスを JavaScript で呼び出すことは許可されていません。この保護が存在しないと、ログインしている任意の Web ページから (Facebook や GitHub などの) 他のサービスにリクエストを送信でき、その時点でログインしていれば、それらのサービスがプライベートデータを受信する可能性があります。幸いにも、このセキュリティメカニズムはデフォルトで組み込まれていますが、このメカニズムにより、現在参照している URL 以外の URL にファイルを送信することも禁止されます。そのため、デフォルトでは、JavaScript を使用して Web サイトから S3 にファイルを送信することはできません。この機能を有効にするには、CORS を使用する必要があります。
CORS はオリジン間リソース共有の略で、基本的には、HTTP リクエストの送信元を指定するための機能です。したがって、S3 バケットへの指示で、JavaScript 経由でサーバーからファイルを受信することを許可する必要があります。
本番環境用と開発用に、2 つの異なるバケットが必要です。開発用バケットで、CORS 設定を変更する必要があります。次に、localhost:3000
で実行されているローカルマシンから AWS へのファイル送信を許可する設定の例を示します。
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>http://localhost:3000</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
AllowedOrigin
のための適切なオリジンセットが本番環境バケットにあることを確認する必要があります。バケットでの CORS サポートの設定についての詳細は、「How Do I Enable CORS On My Bucket?」(バケットで CORS を有効にする方法) を参照してください。
事前署名済み POST
これから使用するのは、AWS ruby gem から生成された事前署名済み POST です。事前署名済み URL は、特定のオブジェクトをバケットにアップロードすることをユーザーや顧客に許可するが、AWS のセキュリティ資格情報または権限を持っていることを要求しない場合に役立ちます。詳細は、ドキュメントの事前署名済み POST に関する記述を参照してください。事前署名済み URL は、基本的には、S3 オブジェクト (この場合は画像) が置かれる場所とそれに対する制約に関するすべての設定を行うための簡単な方法です。
app/controllers/users_controller.rb
を開きます。次のような内容になっています。
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
...
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
end
end
ユーザーを作成および編集するときに @s3_direct_post
変数を使用できるよう、before_action
メソッドでこれを設定します。
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
before_action :set_s3_direct_post, only: [:new, :edit, :create, :update]
...
private
def set_s3_direct_post
@s3_direct_post = S3_BUCKET.presigned_post(key: "uploads/#{SecureRandom.uuid}/${filename}", success_action_status: '201', acl: 'public-read')
end
ここでは多くのことを説明する必要がありますが、まずは presigned_post
メソッドのオプションについて説明します。
事前署名済み POST のオプション: key
キーは、S3 内でオブジェクトが置かれる場所です。S3 ではキーの重複は認められていないため、各キーは一意である必要があります。AWS ではカスタムの ${filename}
ディレクティブがサポートされています。構文は ("#{}
を使用して) Ruby コードを文字列に挿入する方法に似ていますが、これは AWS で特別な意味に解釈される文字列です。この ${filename}
ディレクティブは、ユーザーが pic.png
という名前のファイルをアップロードした場合、S3 は最終的なファイルを pic.png
と同じ名前で保存する必要があることを S3 に指示します。同じ名前のファイルを使用している 2 人のユーザーがどちらもアバターを保存できるようにするために、SecureRandom.uuid
を使用してランダム文字列を追加します。最後に、すべての画像はベースレベルの uploads
に委任されるため、他のアイテムをバケットに保存する場合は、ユーザーがアップロードしたファイルを個別に保管する方が簡単です。
事前署名済み POST のオプション: success_action_status
これは、画像が正常に保存されたときに AWS が返す HTTP ステータスコードです。${filename}
ディレクティブを使用しているため、ファイルの保存に使用されるキーの名前について AWS から通知を受ける必要があります。そのためには、success_action_status
を 201 に設定する必要があります。新しいオブジェクトの作成に関する AWS のドキュメントを参照してください。
値が 201 に設定されている場合、Amazon S3 は 201 ステータスコードを含む XML ドキュメントを返します。
キーを解析できる XML 応答を受信できることが必要です。201 ステータスコードを省略すると、それが不可能になります。この値を 201 に設定することは非常に重要です。
事前署名済み POST のオプション: acl
アクセス制御リスト (ACL) は、誰がファイルを閲覧、編集、削除できるかを AWS で制御する方法です。アバター URL をすべてのユーザーに公開するために、この値を :public_read
に設定します。このように設定すると、ユーザーが S3 バケットにアップロードしたファイルは誰でも閲覧できますが、ファイルを編集できるのは S3 バケットの管理者だけです。acl
を設定しない場合、ユーザーのアバターを誰も閲覧できなくなります。
クライアント側のコード
事前署名済み POST の用意ができたら、クライアント側でオブジェクト内の情報を使用して画像を S3 に送信できます。
サーバーを一時キャッシュとして利用できないため、クライアント側のコード (JavaScript) を使用してファイルを S3 に配信する必要があります。HTML 5 でファイル API が導入されましたが、バージョン 10 までの IE ではサポート対象外でした。これに対する解決策として、jQuery File Upload プラグインを使用します。ただし、このライブラリを使用する前に、まず JQuery UI が必要です。
$ curl -O \
https://raw.githubusercontent.com/jquery/jquery-ui/master/ui/widget.js \
>> app/assets/javascripts/jquery.ui.widget.js
次に、fileupload JavaScript を Rails プロジェクトに含めます。
$ curl -O \
https://raw.githubusercontent.com/blueimp/jQuery-File-Upload/master/js/jquery.fileupload.js \
>> app/assets/javascripts/z.jquery.fileupload.js
名前の先頭に z
を付けて、このファイルが他の jQuery ファイルよりも後に読み込まれるようにしています。
‘//= require_tree’ ディレクティブを使用して application.js
内のすべての JavaScript ファイルを読み込んでいる場合、
この JavaScript は自動的に使用可能になります。そうでない場合、次のようにして明示的に要求する必要があります。
//= require jquery.ui.widget
//= require z.jquery.fileupload
ローカルサーバーを起動します。
$ heroku local
ブラウザで localhost:3000/users/new を読み込み、fileupload JavaScript ファイルの存在を確認します。Chrome では、CMD+Option+J
キーを押して JavaScript コンソールを開くことができます。コンソールで、jQuery が正しく読み込まれていることを確認します。
> console.log($)
function $(selector, [startNode]) { [Command Line API] }
ここで、fileupload
関数が使用できることを確認します。
> console.log($().fileupload)
function ( options ) {
var isMethodCall = typeof options === "string",
args = slice.call( arguments, 1 ),
returnValue = this;
//...
結果が返されない場合は、JavaScript コンソールでエラーが報告されていないこと、ソースを表示したときにすべての必要なファイルがリストに含まれていること、それらのファイルが空でなく順序が正しいことを確認してください。
ビューを準備する
S3 アップロードプロセスを完了するには、まずファイルを S3 に送信してから、URL をデータベースに保存する必要があります。このタスクの場合、Users テーブルに avatar_url
文字列フィールドがあります。
JavaScript では、S3 に直接アップロードする画像が含まれるフィールドを特定する何らかの方法が必要です。まず、ファイルアップロードフィールド app/views/users/_form.html.erb
があることがわかっているフォームを開きます。フォームは次のようになります。
<%= form_for(@user) do |f| %>
<% if @user.errors.any? %>
<div id="error_explanation">
<!-- ... -->
JavaScript でこのフォームをターゲットにする方法が必要なため、directUpload
という名前のカスタムクラスを追加します。
<%= form_for(@user, html: { class: "directUpload" }) do |f| %>
<% if @user.errors.any? %>
<div id="error_explanation">
<!-- ... -->
POST の際に、特定の S3 データも一緒に送信する必要があります。これを data 属性としてフォームに配置し、後から JavaScript で参照します。
<%= form_for(@user, html: { class: 'directUpload', data: { 'form-data' => (@s3_direct_post.fields), 'url' => @s3_direct_post.url, 'host' => URI.parse(@s3_direct_post.url).host } }) do |f| %>
ユーザーが写真をアップロードするためのファイルフィールドも必要です。avatar_url
フィールドを探します。これは text_field
である必要があります。
<div class="field">
<%= f.label :avatar_url %><br>
<%= f.text_field :avatar_url %>
</div>
これを変更して file_field
にします。
<div class="field">
<%= f.label :avatar_url %><br>
<%= f.file_field :avatar_url %>
</div>
クライアント側でのファイルフィールドの検出
この時点で、画像をアップロードするための JavaScript ライブラリ、S3 バケット、有効な事前署名済み POST オブジェクト、avatar_url
文字列フィールドを持つ User モデル、ファイル入力フィールドを持つビューの準備ができています。S3 に送信するユーザーの画像を取得し、URL を avatar_url
に保存する必要があります。これは手動のプロセスであり、主に JavaScript を使用して行います。このエクスペリエンスはカスタマイズが可能であり、そのために使用できるオプションについては後で説明します。
デバッグ用の一連の <script></script>
タグ、または application.js
ファイルのいずれかで、カスタムクラスを含むフォームを探してから、すべてのファイルフィールドを反復処理します。
$(function() {
$('.directUpload').find("input:file").each(function(i, elem) {
var fileInput = $(elem);
console.log(fileInput);
});
});
ここでページを読み込むと、ファイルフィールドごとにコンソール出力が表示されます (avatar_url
に対応する出力だけであることを確認します)。ここから、役に立つ他の要素を引き出すことができます。ファイル入力を保持するフォームと、フォームの送信ボタンが必要であり、ファイル要素ごとの進捗状況バーを作成する必要もあります。
$(function() {
$('.directUpload').find("input:file").each(function(i, elem) {
var fileInput = $(elem);
var form = $(fileInput.parents('form:first'));
var submitButton = form.find('input[type="submit"]');
var progressBar = $("<div class='bar'></div>");
var barContainer = $("<div class='progress'></div>").append(progressBar);
fileInput.after(barContainer);
});
});
ここで、進捗状況バーにスタイルを適用してみましょう。app/assets/stylesheets/screen.css
で、次の内容を追加します。
.progress {
max-width: 600px;
margin: 0.2em 0 0.2em 0;
}
.progress .bar {
height: 1.2em;
padding-left: 0.2em;
color: white;
display: none;
}
完成した jquery-file-upload コード
すべての必要な要素が揃ったので、それぞれのファイル入力要素で fileInput.fileupload({})
を呼び出してオプションを渡すことができます (コールバックは後で追加します)。
$(function() {
$('.directUpload').find("input:file").each(function(i, elem) {
var fileInput = $(elem);
var form = $(fileInput.parents('form:first'));
var submitButton = form.find('input[type="submit"]');
var progressBar = $("<div class='bar'></div>");
var barContainer = $("<div class='progress'></div>").append(progressBar);
fileInput.after(barContainer);
fileInput.fileupload({
fileInput: fileInput,
url: form.data('url'),
type: 'POST',
autoUpload: true,
formData: form.data('form-data'),
paramName: 'file', // S3 does not like nested name fields i.e. name="user[avatar_url]"
dataType: 'XML', // S3 returns XML if success_action_status is set to 201
replaceFileInput: false
});
});
});
ファイルを S3 に送信するために必要なものはこれで全部ですが、画像の URL を取得したり、現在の状況をユーザーに知らせるために UI を更新したりすることはまだできません。これらの機能はすべて、後の「コールバック」のセクションで追加します。
まずは、fileupload
に渡すオプションについて説明します。詳細は、jquery-file-upload のオプションに関するドキュメントを参照してください。
オプション: fileInput
このオプションは jQuery-File-Upload がファイルを探すことができる場所の参照であり、今回の例ではフォームのファイル入力フィールドからファイルを取得します。
オプション: url
画像の送信先 URL です。以前に AWS 事前署名済み POST を設定するときに生成した URL に送信します。この情報は、以前にフォームフィールドに設定した data 属性から読み取ります。
url: form.data('url')
オプション: type
使用する HTTP リクエストの種類です。今回の例では S3 上にオブジェクトを “作成” するので、POST
HTTP リクエストを送信する必要があります。
オプション: autoUpload
ユーザーがファイルを選択したら、自動的にアップロードを開始します。これにより、他にも多くの情報を入力する必要がある場合でも、フォームの残り項目の入力中に画像をアップロードできます。画像のアップロードの状況を知らせるためには、手動で進捗状況バーを描画する必要があります。
この値を false
に設定した場合、JavaScript によってアップロードアクションを手動でトリガーする必要があります。
オプション: formData
URL の生成に加えて、事前署名済み POST は、画像を送信するために必要なすべてのデータ (AWS アクセスキーなど) をセットアップします。このオブジェクトにすでに含まれている情報を利用するために、以前に設定した data 属性からこれを読み取ります。
formData: form.data('form-data')
オプション: paramName
送信中のフィールドの “名前” です。デフォルトでは、Rails は name="user[avatar_url]"
のような HTML を生成します。S3 ではこのようなネストされた名前フィールドは望ましくないため、他の名前を付けます。今回の例では “file” を使用します。
paramName: 'file'
オプション: dataType
画像のアップロード後に返されるデータの想定型を指定します。success_action_status
が 201 に設定されている場合、AWS から XML
が返されるため、ここでは XML が返されることを想定するよう jQuery-File-Upload に指示します。
オプション: replaceFileInput
デフォルトでは、jQuery-File-Upload は画像入力を複製し、ページ上の画像入力を複製で置き換えます。これによって予期しない視覚的動作が発生し、後から必要な場合に fileInput
オブジェクトを操作することが難しくなります。これを避けるために、この機能を無効にします。
jQuery-File-Upload のコールバック
progressall
、start
、done
、fail
の各オプションで設定された多数のコールバックを使用します。これらは、アップロードの進捗状況をユーザーに表示したり、UI を制御したり、フォーム送信時に avatar_url
の適切な値を設定したりするために必要です。コールバックの完全なコードは次の場所にあります。
$(function() {
$('.directUpload').find("input:file").each(function(i, elem) {
var fileInput = $(elem);
var form = $(fileInput.parents('form:first'));
var submitButton = form.find('input[type="submit"]');
var progressBar = $("<div class='bar'></div>");
var barContainer = $("<div class='progress'></div>").append(progressBar);
fileInput.after(barContainer);
fileInput.fileupload({
fileInput: fileInput,
url: form.data('url'),
type: 'POST',
autoUpload: true,
formData: form.data('form-data'),
paramName: 'file', // S3 does not like nested name fields i.e. name="user[avatar_url]"
dataType: 'XML', // S3 returns XML if success_action_status is set to 201
replaceFileInput: false,
progressall: function (e, data) {
var progress = 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 response
var key = $(data.jqXHR.responseXML).find("Key").text();
var url = '//' + form.data('host') + '/' + key;
// create hidden field
var input = $("<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");
}
});
});
});
フォーム送信時に適切な avatar_url
をデータベースに設定したり、S3 への画像アップロード中に読み込みの進捗状況バーを表示したり、画像アップロードの進行中にユーザーが誤ってフォームを送信するのを防いだりするために必要なものはこれで全部です。
デバッグ用の console.log("done");
コードは、コールバックに追加しておくと、コールバックが適切に呼び出されていることの確認に役立ちますが、本番環境にデプロイする前に削除することを忘れないでください。
次のセクションでは、各コールバックとそれに含まれるコードについて説明します。
コールバック: progressall
progressall
コールバックは、イベントオブジェクトとデータオブジェクトを使用して呼び出されます。データオブジェクトには、アップロードされるファイルの合計サイズ data.total
と、現在までにアップロード済みのファイルサイズ data.loaded
が含まれます。これら 2 つを使用して進捗率を計算し、進捗状況バーを使用して表示できます。
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
progressBar.css('width', progress + '%')
}
すべての画像入力に対して進捗状況バーを作成し、画像ごとに個別の fileupload
を初期化するため、この progressall
コールバックには 1 つの画像に関する情報しか含まれないことに注意してください。
コールバック: start
このコールバックはファイルアップロードの開始時に呼び出されます。これを使用して進捗状況バーを表示します。また、画像が途中までしかアップロードされていなのにユーザーが誤ってフォームを送信しないよう、送信ボタンを無効にします。
start: function (e) {
submitButton.prop('disabled', true);
progressBar.
css('background', 'green').
css('display', 'block').
css('width', '0%').
text("Loading...");
}
コールバック: done
このコールバックは、画像が正常に S3 に送信されると呼び出されます。
コールバック全体は次のようになります。
done: function(e, data) {
submitButton.prop('disabled', false);
progressBar.text("Uploading done");
// extract key and generate URL from response
var key = $(data.jqXHR.responseXML).find("Key").text();
var url = '//' + $(elem).data('host') + '/' + key;
// create hidden field
var input = $("<input />", { type:'hidden', name: fileInput.attr('name'), value: url })
form.append(input);
}
短く、機能は複雑ですが、処理内容は次のとおりです。
まず、ファイルのアップロードが完了したので、送信機能を再度有効にします。
submitButton.prop('disabled', false);
進捗状況バーのテキストも更新します。
progressBar.text("Uploading done");
最も重要なのは、S3 からの XML 応答を解析してキーを取得することです。
var key = $(data.jqXHR.responseXML).find("Key").text();
キーを取得したら、image: からのプロトコル相対 URL を構築します。
var url = '//' + form.data('host') + '/' + key;
このデータを使用して、URL 文字列の値と元の要素の名前を持つ非表示フィールドを作成します。fileInput.attr('name')
を使用して要素から名前を取り出すことができます。
var input = $("<input />", { type:'hidden', name: fileInput.attr('name'), value: url })
そのため、新しい入力要素は同じ名前になります (この場合は name="user[avatar_url]"
)。したがって、ユーザーがフォームを送信すると、URL が UserController の create
アクションに返送され、保存されます。
form.append(input);
user[avatar_url]
の名前はすでにファイルフィールドで使用されていますが、非表示要素の方が (フォームで後ろの方に出現するので) 優先されます。この時点で、ユーザーがフォームを送信すると、URL がパラメータに保存されます。
コールバック: fail
問題が発生して画像がアップロードされない場合、アップロードの失敗をユーザーに警告し、(画像が機能しなくても送信する必要がある場合に備えて) 送信ボタンを再び有効にする必要があります。
今回の例では、進捗状況バーの色を赤に変えて、失敗テキストを出力することによってこれを行います。
fail: function(e, data) {
submitButton.prop('disabled', false);
progressBar.
css("background", "red").
text("Failed");
}
画像の送信と描画
画像が S3 に正常にアップロードされ、非表示のフォーム要素がフォームにアタッチされたら、ユーザーはフォームを送信できます。これが正しく機能すると、ログのパラメータで user["avatar_url"]
を確認できます。
Started POST "/users" for 127.0.0.1 at 2014-05-19 17:47:01 -0500
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "user"=>{"avatar_url"=>"//my-development.s3.amazonaws.com/uploads/220f5378-1e0f-4823-9527-3d1170089a49/foo.gif", "name"=>"Schneems", "twitter"=>""}, "commit"=>"Create User"}
この時点で、URL の表示を希望し、@user
オブジェクトがある場合、画像を描画できます。
<%= image_tag @user.avatar_url %>
デバッグ
S3 への直接アップロードを実装している間、JavaScript コンソールを開いたままにし、console.log
ステートメントを追加して開発中のコールバックの実行可能性を高めると非常に役立ちます。それらは本番環境にデプロイする前に削除する必要があります。コンソールに例外がある場合、解決しない限りコードは機能しません。ファイルのアップロードは機能しているが URL が保存されない場合は、ログをチェックし、UsersController の create
アクションのコードが avatar_url
列を適切に保存および許可していることを確認してください。
拡張
この例は、S3 への直接アップロードを実装する 1 つの方法を表しています。要件や需要はアプリケーションによって異なります。この例をテンプレートとして使用し、変更および拡張して、必要な動作を実現する必要があります。
たとえば、クライアント側での写真のトリミングや編集を有効にしたり、さまざまな進捗状況バー、ドラッグアンドドロップインターフェイスなどを使用したりできます。コードを拡張する方法については、jQuery-File-Upload のドキュメントを参照してください。
受け入れ可能なファイルの種類を制限する方法や、一定時間の経過後にユーザーの画像を期限切れにする方法については、AWS 事前署名済み POST に関するドキュメントを参照してください。