Active Storageで画像をアップロード
Active Storageとは
ファイルアップロードを簡単に実装できるgemです。
railsの標準のgemで、rails5.2から追加されました。
scaffoldアプリの作成
rails new active_storage_app
名前はactive_storage_app
としておきます。
rails new
したら
cd active_storage_app
で移動して
rails g scaffold User name:string rails db:migrate
scaffoldで元になるアプリを作成します。
Active Storageの導入
rails active_storage:install
このコマンドでactive_storage_blobs
とactive_storage_attachments
の2つのテーブルが作成されるマイグレーションファイルが生成されるので
rails db:migrate
とします。
active_storage_blobs
はアップロードしたファイルを保存するテーブルで
active_storage_attachments
は中間テーブルです。
一人のユーザーに対して一枚のアバター画像をもつ1対1の関係のとき、
モデルにhas_one_attached :カラム名
を記述します。
class User < ApplicationRecord has_one_attached :avator end
カラム名、と言いましたが、usersテーブルにはnameカラムしか作っていませんでした。
しかし、わざわざマイグレーションファイルを作成してavatorカラムを追加して…としなくても、
has_one_attached
を書くだけで大丈夫です。
画像サイズの設定
画像以外に、画像のサイズを設定できるようにもしたいので、
rails g migration add_avator_width_to_users
でマイグレーションファイルを作成し、
class AddAvatorWidthToUsers < ActiveRecord::Migration[5.2] def change add_column :users, :avator_width, :integer end end
usersテーブルにinteger型のavator_widthカラムを追加し、
rails db:migrate
とします。
画像のサイズはvalidationで100px~500pxに制限しておきます。
また、avator_widthを空欄で登録しようとすると弾かれてしまうのでallow_nil: true
も書いておきます。
class User < ApplicationRecord has_one_attached :avator validates :avator_width, numericality: { greater_than_or_equal_to: 100, less_than_or_equal_to: 500 }, allow_nil: true end
コントローラのストロングパラメータでは
class UsersController < ApplicationController # 略 def user_params params.require(:user).permit(:name, :avator, :avator_width) end end
avator
とavator_width
をpermitしておきます。
viewを整形
viewの方も整えていきます。
画像ファイルと画像サイズのフォームを作成し
_form.html.erb <%= form_with(model: user, local: true) do |form| %> <% if user.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2> <ul> <% user.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= form.label :name %> <%= form.text_field :name %> </div> <div class='field'> <%= form.label :avator %> <%= form.file_field :avator %> </div> <div class='field'> <%= form.label :avator_width %> <%= form.number_field :avator_width %> </div> <div class="actions"> <%= form.submit %> </div> <% end %>
ユーザー詳細ページでそれを表示させます。
show.html.erb <p id="notice"><%= notice %></p> <p> <strong>Name:</strong> <%= @user.name %> </p> <% if @user.avator.attached? %> <%= image_tag @user.avator, width: @user.avator_width %> <% end %> <%= link_to 'Edit', edit_user_path(@user) %> | <%= link_to 'Back', users_path %>
attached?
メソッドはhas_one_attached
を設定することで使えるメソッドです。
userがavatorを持っていればtrueを、持っていなければfalseを返します。
ユーザー詳細ページを見てみると
ちゃんと画像が表示されていますね。
画像サイズを調整
ユーザー詳細ページのアバター画像には、フォームに入力した画像サイズが適用されています。
ユーザー一覧ページにもアバターを表示させたいですが、小さいサイズに調整したいです。
画像処理ツールのImageMagick
、ImageMagickをrailsで使うためのgem、mini_magick
を使います。(ImageMagickのインストール方法は省略します)
Gemfileにmini_magickがコメントアウトされていると思うので、コメントアウトを外してbundle install
したら、
ユーザー一覧ページを編集して、サイズ調整した画像を表示させましょう。
<p id="notice"><%= notice %></p> <h1>Users</h1> <table> <thead> <tr> <th>Name</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @users.each do |user| %> <tr> <td><%= user.name %></td> <td><%= image_tag user.avator.variant(resize: '50x50').processed if user.avator.attached? %></td> <td><%= link_to 'Show', user %></td> <td><%= link_to 'Edit', edit_user_path(user) %></td> <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </tbody> </table> <br> <%= link_to 'New User', new_user_path %>
variant(resize: 50x50)
で画像サイズを変換しています。
processed
はすでにそのサイズで保存されている画像があれば変換処理は行われず、すぐにURLが返されます。
最初の呼び出しだけ多少時間がかかりますが、それ以降の呼び出しでは時間がかかることはありません。
ではユーザー一覧ページを見てみましょう。
画像サイズが変換されて表示されていますね。
しかしもしアプリの機能が増えて色んなページで画像を表示するとなったときに書き換えるのが大変です。
Decoratorを使って表示に関する処理を切り出してみます。
gem ActiveDecoratorを導入
Gemfile gem 'active_decorator'
としてbundle install
します。
次にでUserモデルに対するdecoratorを作成します。
rails g decorator user
このコマンドでapp/decorators/user_decorator.rbというファイルが生成されました。
このファイルにviewで呼び出すメソッドを書いていきます。
module UserDecorator def avator_url(version = :origin) command = case version when :small { resize: '50x50' } when :midium { resize: '150x150' } when :large { resize: '300x300' } else false end command ? avator.variant(command).processed : avator end end
viewで呼び出す際は
<%= image_tag user.avator_url(:midium) if user.avator.attached? %>
このようにすることで引数によってサイズを変えることができます。
<%= image_tag user.avator_url(:large) if user.avator.attached? %>
これなら引数を変えてあげるだけですね。
最後に
実際にrails newから作ってみると理解しやすかったです。
小さなアプリだとあまり恩恵がないですが規模が大きくなるほどメンテナンス性などを考える必要がありそうです。
最後まで読んでいただきありがとうございました。
参考サイト
【Rails】 Active | Pikawaka - ピカ1わかりやすいプログラミング用語サイト
Active StorageのVariantの指定方法いろいろ - Qiita
【Ruby on Rails】Gem「ActiveDecorator」の紹介 View向けのメソッドを定義する - ざきの学習帳(旧 zackey推し )