Rails5 Active Storageを使って画像アップロード機能を実装する
ファイルアップロード機能の実装方式
Rails5.1以前においては以下のgemを使用するのが有力な選択肢だった。
- CarrierWave
GitHub - carrierwaveuploader/carrierwave: Classier solution for file uploads for Rails, Sinatra and other Ruby web frameworks - shrine
GitHub - shrinerb/shrine: File Attachment toolkit for Ruby applications - Paperclip
GitHub - thoughtbot/paperclip: Easy file attachment management for ActiveRecord - Refile
GitHub - refile/refile: Ruby file uploads, take 3
Rails 5.2になって、Rails本体の機能としてActive Storageが追加されたことで、これが上記の選択肢に加わった。
Active Storage
rails/activestorage at master · rails/rails · GitHub
概要はこちらのRails Guideを参照されたい。
Active Storage Overview — Ruby on Rails Guides
で、どれを使えばいいのか?
Active Storageはビルトインならではの簡便さが魅力であるものの、CarrierWaveやShrineと比較すると、まだ機能的に未成熟のようだ。(自分で触ったわけではないが)
なので要件に照らして、Active Storageで事足りる、あるいは機能不足を許容できるのであれば、お手軽に使えるActive Storageを使えばよいし、そうでなければCarrierWaveやShrineを選ぶことになるだろう。
今後の変化の激しさも考えると、現時点でActive Storageを採用するのは悪手な気もするのだが、 今回はお試しということでActive Storageを使ってみることにした。
環境
- Rail 5.2.1
- Active Storage 5.2.1
ファイルアップロード機能実装に必要 - MiniMagick 4.9.2
画像の加工に必要 - ImageMagick 6.7.8.9-15
MiniMagickが使用。OSにインストールする。
Active Storageを利用するにはRails 5.2.0以上にアップデートしなければならない。
5.1.5からのアップデート方法であれば、こちらの記事に非常にわかりやすくまとまっている。
qiita.com
画像アップロード機能の要件
実装
以下、rails newで新規プロジェクトを作成した直後の状態を想定する。
MiniMagickのgemを追加
Gemfileに以下を追記
# Gemfile gem 'mini_magick', '~> 4.8'
$ bundle install
Active Storageが使用するtableを作成
Active Storageはファイル添付機能の実装にあたり、添付先のモデルにカラムの追加を必要としない。代わりにBlobとAttachmentの2つのモデルを用いる。
- Blob(Binary Large Object):
ファイルのメタ情報を管理。ファイルの実態はローカルファイルシステムまたはS3などのクラウドサービスに格納される。Blobが管理する情報は以下。- id
- key
- filename
- content_type
- metadata
- byte_size
- checksum
- created_at
- Attachment:
添付先のモデルとBlobを紐づける中間テーブルとしての役割を担う。Attachementが管理する情報は以下。- id
- name
- record_type
- record_id
- blob_id
- created_at
上記2つのモデルを以下の2コマンドで作成できる。
# migrationファイルの作成
$ rails active_storage:install
# テーブルの作成
$ rails db:migrate
scaffoldでmodel, view, controller等を作成
$ rails g scaffold post title body:test
routeの追加
# config/routes.rb root to: 'posts#index'
modelの編集
# app/model/post.rb has_many_attached :images validate :image_type def thumbnail input return self.images[input].variant(resize: '300x300!').processed end private def image_type if images.each do |image| if !image.content_type.in?(%('image/jpec image/png')) errors.add(:images, 'needs to be a JPEG or PNG') end end end
- validate :image_type , def image_type ファイルタイプでバリデーションを行う。今回はjpegとpngファイルのみアップデートを許可し、それ以外はerrorを返す。
- thumbnailメソッド:
アップロードされた画像をオリジナルサイズで扱うのではなく、リサイズしてサムネイルとして表示するためのメソッド。processedを使用することで、毎回リサイズの処理を走らせずに済む。- input(引数):
複数画像を添付可能とするため、何枚目の画像かをinputという引数で指定できるようにしておく。(さらにいえば、リサイズ処理におけるサイズ指定を変数化して、第二引数、第三引数で指定できるようにしておくとよい。本例では省略。) - resize: '300x300!':
リサイズのサイズ指定。"!"を付けるとオリジナル画像のアスペクト比を無視して、指定されたサイズによるリサイズを強制する。
- input(引数):
controllerの編集
# app/controller/posts_controller.rb # privateメソッドのpost_paramsを修正 def post_params params.require(:post).permit(:title, :body, images: []) end
viewの編集
_formページ
viewのフォームに画像アップロード用のフィールドを追加する。
複数アップロードを可能にしたいので、multiple: trueが必要。
# app/view/posts/_form.html.erb <%= form.label :images %> <%= form.file_field :images, multiple: true %>
showページ
# app/vie/posts/show.html.erb <% (0...@post.images.count).each do |image| %> <%= image_tag(@post.thumbnail(image) %> <% end %>