This article was contributed by Tute Costa
Tute is a developer at thoughtbot NYC. You can find him on twitter @tutec .
Paperclip を使用した Ruby での S3 へのファイルのアップロード
最終更新日 2022年03月09日(水)
Table of Contents
多くの Web アプリでは、ユーザーは画像やその他のファイルを保存または処理のためにアップロードする必要があります。Paperclip は、ファイルのアップロードや処理の複雑さを軽減する、きれいに抽象化された Ruby ライブラリです。
Paperclip を Amazon S3 や Rackspace CloudFiles などの外部ストレージサービスと共に使用すると、アプリケーションのファイルとコードベースを独立してスケーリングできます。このようなアーキテクチャは、Heroku などの、アプリケーションを複数のインスタンスにわたって分散させる柔軟性の高い環境で必要になります。
このガイドでは、Ruby on Rails アプリケーションを Paperclip と Amazon S3 を使用した画像のアップロードと共に設定する方法について説明します。
前提条件
- クラウドに画像を保存するための AWS S3 アカウント。
- Web アプリケーションを作成して Heroku にデプロイするための Heroku CLI。
- 画像のサイズを変更するための ImageMagick。
注意: Mac ユーザーは、Homebrew の brew install imagemagick
を使用して ImageMagick をインストールできます。Windows ユーザーは、Windows バイナリリリースを使用できます。
概要
Paperclip は、ActiveRecord
のための簡単なファイル添付ライブラリです。これは、ファイルをモデル属性のように扱います。つまり、ファイルは ActiveRecord::Base#save
が呼び出されるまで、最終的な場所に保存されたり、nil
に設定されている場合に削除されたりしません。これは、ファイルのサイズと存在に基づいて検証できます。割り当てられている画像を必要に応じてサムネイルに変換することができ、唯一の前提条件はデータベース列と ImageMagick です。添付ファイルは、妥当なデフォルト値を持つ理解可能な指定によってブラウザで参照されます。
リファレンスアプリケーション
このリファレンスアプリケーションを使用すると、ユーザーは自分の友だちの一覧を管理できます。
- 各友だちには、画像のアップロードとサイズ変更の機能を提供する Paperclip の
avatar
が割り当てられます。 - このアプリは、スケールダウンされたサムネイルを生成したり、サイズが変更された画像を表示したりする方法を示します。
- また、このアプリケーションは緩やかに縮退して、アバターなしの友だちのデフォルト画像
missing.png
を表示します。
ソースコードは、GitHub からダウンロードしてください。
設定
Paperclip では、Gemfile に次の gem を追加する必要があります。
# Gemfile
gem 'paperclip'
gem 'aws-sdk', '~> 2.3'
Gemfile を変更した後、bundle install
を実行して Rails サーバーを再起動します。
また、本番環境の AWS 環境設定も指定する必要があります。
# config/environments/production.rb
config.paperclip_defaults = {
storage: :s3,
s3_credentials: {
bucket: ENV.fetch('S3_BUCKET_NAME'),
access_key_id: ENV.fetch('AWS_ACCESS_KEY_ID'),
secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY'),
s3_region: ENV.fetch('AWS_REGION'),
}
}
S3 へのアップロードを開発モードでローカルにテストするには、これらの設定を開発環境にも追加する必要があります。
さらに、Heroku アプリケーションで AWS 環境設定を設定する必要があります。
$ heroku config:set S3_BUCKET_NAME=your_bucket_name
$ heroku config:set AWS_ACCESS_KEY_ID=your_access_key_id
$ heroku config:set AWS_SECRET_ACCESS_KEY=your_secret_access_key
$ heroku config:set AWS_REGION=your_aws_region
国際的なユーザー (追加の設定)
画像のアップロードで問題が発生する場合は、次の 2 つの設定セクションを参照してください。
引き続き問題が発生する場合は、詳細な設定オプションについて Paperclip のドキュメントのページを参照してください。
デフォルトの URL 構造を上書きするには、バケットの名前の “ドメイン形式” を URL に含めます (たとえば、your_bucket_name.s3.amazonaws.com)。これらのオプションは、上記の paperclip_defaults
設定ハッシュ、またはイニシャライザに追加できます。
# config/initializers/paperclip.rb
Paperclip::Attachment.default_options[:url] = ':s3_domain_url'
Paperclip::Attachment.default_options[:path] = '/:class/:attachment/:id_partition/:style/:filename'
次のエラー “The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.” (アクセスしようとしているバケットは、指定されたエンドポイントを使用してアドレス指定されている必要があります。将来のすべてのリクエストをこのエンドポイントに送信してください。) が表示される場合は、指定されたエンドポイントを s3_host_name
環境設定で設定してみてください。
# config/initializers/paperclip.rb
Paperclip::Attachment.default_options[:s3_host_name] = 's3-us-west-2.amazonaws.com'
モデルのファイル属性の定義
Friend
モデルに添付機能を追加するには、Paperclip ヘルパーメソッド has_attached_file
と記号を添付の望ましい名前と共に使用します。
class Friend < ActiveRecord::Base
# This method associates the attribute ":avatar" with a file attachment
has_attached_file :avatar, styles: {
thumb: '100x100>',
square: '200x200#',
medium: '300x300>'
}
# Validate the attached image is image/jpg, image/png, etc
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
end
has_attached_file
メソッドはまた、アップロードされた画像のサイズ変更ディメンションを指定する styles
ハッシュも受け付けます。>
と #
の記号は、ImageMagick に画像のサイズの変更方法を指示します (>
は画像のサイズを比例的に小さくする)。
データベースの更新
Friend
の avatar
属性をデータベーススキーマに追加するには、データベース移行が必要です。スタブ移行を生成するには、次の Rails ヘルパーメソッドを実行します。
$ rails g migration AddAvatarToFriends
Paperclip には、移行ヘルパーメソッド add_attachment
と remove_attachment
が付属しています。これらは、データベースに画像データを保存するために必要な列を作成するために使用されます。これらは、AddAvatarToFriends
の移行で使用します。
class AddAvatarToFriends < ActiveRecord::Migration
def self.up
add_attachment :friends, :avatar
end
def self.down
remove_attachment :friends, :avatar
end
end
この移行では、Friend
モデルの avatar_file_name
、avatar_file_size
、avatar_content_type
、および avatar_updated_at
属性が作成されます。これらの属性は、ファイルがアップロードされるときに自動的に設定されます。
データベースを更新するには、rake db:migrate
を使用して移行を実行します。
アップロードフォーム
画像は、S3 に保存される前にアプリケーションにアップロードされます。これにより、S3 に送信される前に、モデルで検証やその他の処理を実行できます。
Web フォームに、ユーザーがローカルファイルシステムから画像を参照したり、選択したりできるファイル入力フィールドを追加します。
このフォームに multipart: true
が追加されていることを確認してください。
<%= form_for(@friend, multipart: true) do |f| %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :avatar %>
<%= f.file_field :avatar %>
</div>
<div class="actions">
<%= f.submit 'Make a friend' %>
<%= link_to 'Nevermind', friends_path, class: 'button' %>
</div>
<% end %>
フォームが送信され、バッキングモデルがデータベースに正常に保存されると、そのファイル自体が S3 にアップロードされます。
アップロードコントローラ
Rails 4 では、許可されるパラメータを指定する必要があります。params で :name
と :avatar
を許可します。
class FriendsController < ApplicationController
# Other CRUD actions omitted
def create
@friend = Friend.new(friend_params)
if @friend.save
redirect_to @friend, notice: 'Friend was successfully created.'
else
render action: 'new'
end
end
private
def friend_params
params.require(:friend).permit(:avatar, :name)
end
end
(Rails などの) シングルスレッドの非イベント型環境で大きなファイルをアップロードすると、アプリケーションの Web dyno がブロックされ、リクエストのタイムアウトや H11、H12 エラーが発生する可能性があります。4 MB より大きなファイルの場合は、代わりに直接アップロードの方法を使用してください。
画像の表示
Paperclip を使用してアップロードされたファイルは S3 に保存されます。ただし、ファイルの名前、S3 上の場所、最終更新日のタイムスタンプなどのメタデータはすべて、データベース内のモデルのテーブルに保存されます。
モデルのファイル属性 (この例では avatar
) の url
メソッドを使用してファイルの URL にアクセスします。
friend.avatar.url #=> https://your_bucket_name.s3.amazonaws.com/...
この URL は、アップロードされた画像を表示するためのビューで直接使用できます。
<%= image_tag @friend.avatar.url(:square) %>
url
メソッドは、ファイルの特定の処理されたバージョンにアクセスするための (Friend
モデルで以前に定義された) スタイルを受け取ることができます。
これらの画像は S3 から直接処理されるため、他のリクエストを妨げることがなく、ページではアプリから直接処理される場合よりすばやくロードできます。
url
メソッドに :medium
を渡すことによって、中程度のサイズの画像を表示します。
<%= image_tag @friend.avatar.url(:medium) %>
デプロイ
Paperclip を使用するようにアプリケーションを更新したら、変更されたファイルを Git にコミットします。
$ git commit -m "Upload friend images via Paperclip"
Heroku へのデプロイでは、必要なファイル列をサポートするためにデータベースを移行する必要があります。
$ git push heroku master
$ heroku run bundle exec rake db:migrate