Edited at

基礎Ruby on Rails #22 Chapter13 ファイルのアップロード

More than 1 year has passed since last update.

基礎Ruby on Rails #21 Chapter12 アセット・パイプライン

基礎Ruby on Rails #23 Chapter13 複数画像のアップロード/順番の入れ替え


Active Storageとは


Active Storageのしくみ


  • Rails 5.2で導入された機能

  • Amazon S3、Google Cloud Storage、Microsoft Azure Storage等のクラウドスレレージへ簡単にアップロードできる。

  • クラウドストレージに保存されたファイルを配信する手段も提供する。

  • 事前署名付きURLにより配信され、有効期限(5分)が設定されている。一般に公開された状態にはならない。


ディスクサービス


  • Active Storageは、ローカルサーバーのディスクにファイルをアップロードすることもできる。アップロード先をディスクサービスという。

  • ディスクサービスも個別のURLが与えられ、リダイレクションが行われるが、有効期限は設定されない。


セットアップ手順


ImageMagickのインストール


  • Active Storageには画像のサイズを変換する機能があり、利用するにはImage Magickが必要なので、インストールする。


WSL/Ubuntu

$ sudo apt-get install -y imagemagick



Mac

$ brew install imagemagick



Gemパッケージmini_magickの導入


  • mini_magickを導入するために、Gemfileのコメントアウトを外す。


Gemfile(一部)

# Use ActiveStorage variant

gem 'mini_magick', '~> 4.8'

$ bundle install


マイグレーションスクリプトの生成と実行


  • 以下のコマンドを実行して、Active Storage Tableを作成して、マイグレーションスクリプトを実行する。

$ bin/rails active_storage:install

Copied migration 20181015125809_create_active_storage_tables.active_storage.rb from active_storage

$ bin/rails db:migrate

Copied migration 20181015125809_create_active_storage_tables.active_storage.rb from active_storage
== 20181015125809 CreateActiveStorageTables: migrating ========================
-- create_table(:active_storage_blobs)
-> 0.0022s
-- create_table(:active_storage_attachments)
-> 0.0014s
== 20181015125809 CreateActiveStorageTables: migrated (0.0048s) ===============


  • 以上で、Active Storageの準備が整った。


プロフィール画像のアップロードと表示


Memberモデルの拡張


クラスメソッドhas_one_attached、クラスメソッドattribute


  • 属性profile_picture


    • Active Storageが提供するクラスメソッドhas_one_attachedを使うと、モデルオブジェクトに対して1個のファイルを添付できるようになる。

    • 1個のファイルを添付するための属性をモデルクラスに追加する。has_one_attached :profile_pictureを追加する。属性profile_pictureが追加される。



  • 属性new_profile_picture


    • モデルに読み可能な属性である、attribute :new_profile_pictureを追加する。あたかもnew_profile_pictureがあるようにプログラミングできる。一時的に保存するために使用する。




app/models/member.rb(一部)

class Member < ApplicationRecord

has_secure_password

has_many :entries, dependent: :destroy
has_one_attached :profile_picture
attribute :new_profile_picture



「プロフィール画像」フィールドの設置


  • tableタグ内の最上位に、以下のファイルアップロード用部品を追加する。


app/views/shared/_member_form.html.erb(一部)

  <tr>

<th><%= form.label :new_profile_picture %></th>
<td><%= form.file_field :new_profile_picture %></td>
</tr>


  • 日本語ローケールテキストに、new_profile_picture: プロフィール画像を追加する。


config/locales/ja.yml(一部)

    attributes:

member:
new_profile_picture: プロフィール画像


  • MembersControllerのストロング・パラメーターに:new_profile_pictureを追加する。


app/controllers/members_controller.rb(一部)

  private def member_params

attrs = [
:new_profile_picture,


  • AccountsControllerも同様、ストロング・パラメーターに:new_profile_pictureを追加する。


app/controllers/accounts_controller.rb(一部)

  private def account_params

params.require(:account).permit(
:new_profile_picture,


  • まだアップロード機能は実装していないが、とりあえず適当な画像を選択して、更新してもエラーが出なければOK。

image.png


画像アップロード機能の実装


  • モデルクラスに、以下のメソッドを追加する。これで画像アップロード機能が動くようになる。

  • いきなりprofile_pictureに入れてしまうと、アップロード時に即保存されてしまうので、new_profile_pictureprofile_pictureに分けて、バリデーションを働かせる必要がある。


app/models/member.rb(一部)

  before_save do

if new_profile_picture
self.profile_picture = new_profile_picture
# self.profile_picture.attach(new_profile_picture) でも可
end
end


アップロードされた画像の表示



  • @member.profile_picture.attached?は、プロフィール画像が添付されているかどうか。


  • .variant(resize: "128x128")は、128x128ピクセル内で最も大きいサイズで出力する。


app/views/members/_body.html.erb(一部)

<table class="attr">

<tr>
<th>プロフィール画像</th>
<td>
<% if @member.profile_picture.attached? %>
<%= image_tag @member.profile_picture.variant(resize: "128x128") %>
<% end %>
</td>
</tr>


  • 画像が保存されたことを確認。

image.png


シードデータ



  • db/seeds/development/profile.pngを置く。

  • 以下を、シードデータの一番下に追記する。


db/seeds/development/members.rb(一部)

filename = "profile.png"

path = Rails.root.join(__dir__, filename)
m = Member.find_by!(number: 10)

File.open(path) do |f|
m.profile_picture.attach(io: f, filename: filename)
end


$ bin/rails db:rebuild


  • ファイルが入っていることを確認。

image.png


ファイルのデータ形式に関するバリデーション


  • 画像以外のファイルが登録されたとき、表示時にエラーが出るのでバリデーションを追加する。

  • モデルの親クラスApplicationRecordに、ALLOWED_CONTENT_TYPESという配列の定数を追加する。


app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base

self.abstract_class = true

ALLOWED_CONTENT_TYPES = %q{
image/jpeg
image/png
image/gif
image/bmp
}

end



  • 以下のように、バリデーションを追加する。


app/models/member.rb(一部)

  validate if: :new_profile_picture do

if new_profile_picture.respond_to?(:content_type)
unless new_profile_picture.content_type.in?(ALLOWED_CONTENT_TYPES)
errors.add(:new_profile_picture, :invalid_image_type)
end
else
errors.add(:new_profile_picture, :invalid)
end
end


  • 日本語ローケールテキストに、new_profile_picture: プロフィール画像を追加する。


config/locales/ja.yml(一部)

    errors:

messages:

invalid_image_type: にはJPEG、PNG、GIF、BMP形式の画像を指定してください。



  • バリデーションが動いたことが確認できた。

image.png


プロフィール画像の削除


添付ファイルの削除


  • モデルオブジェクトに添付されたファイルを削除するには、purgeメソッドを使う。

$ bin/rails c

irb(main):002:0> m.profile_picture.attached?
#(省略)
=> true
irb(main):003:0> m.profile_picture.purge
Disk Storage (0.4ms) Deleted file from key: GMqCoLJjozzq5KVq3jMxUffM
Disk Storage (0.6ms) Deleted files by key prefix: variants/GMqCoLJjozzq5KVq3jMxUffM/
#(省略)
=> nil
irb(main):004:0> m.profile_picture.attached?
=> false


  • 削除されていることが確認した。

image.png


  • モデルに画像削除のための新項目remove_profile_pictureを追加する。


  • elsif remove_profile_pictureがtrueになったとき、self.profile_picture.purge(削除)の処理を追加。


app/models/member.rb(一部)

  has_many :entries, dependent: :destroy

has_one_attached :profile_picture
attribute :new_profile_picture
attribute :remove_profile_picture, :boolean
#(省略)
before_save do
if new_profile_picture
self.profile_picture = new_profile_picture
# self.profile_picture.attach(new_profile_picture) でも可
elsif remove_profile_picture
self.profile_picture.purge
end
end


チェックボックスの設置


  • もし画像が添付されている場合は、その画像とともに削除用にチェックボックスを設置する。


app/views/shared/_member_form.html.erb(一部)

  <tr>

<th><%= form.label :new_profile_picture %></th>
<td>
<%= form.file_field :new_profile_picture %>
<% if @member.profile_picture.attached? %>
<div>
<%= image_tag @member.profile_picture.variant(resize: "128x128") %>
<%= form.check_box :remove_profile_picture %>
<%= form.label :remove_profile_picture %>
</div>
<% end %>
</td>
</tr>


  • ロケールテキストに、日本語でのラベル名remove_profile_picture: 画像を削除を追加する。


config/locales/ja.yml(一部)

    attributes:

member:
new_profile_picture:
プロフィール画像
remove_profile_picture: 画像を削除



  • :remove_profile_pictureのストロング・パラメータを追加する。AccountsControllerとMembersControllerに追加する。


app/controllers/accounts_controller.rb(一部)

  private def account_params

params.require(:account).permit(
:new_profile_picture,
:remove_profile_picture,
:number,
:name,
:full_name,
:sex,
:birthday,
:email
)
end


app/controllers/members_controller.rb(一部)

  private def member_params

attrs = [
:new_profile_picture,
:remove_profile_picture,
:number,
:name,
:full_name,
:sex,
:birthday,
:email,
:administrator
]


  • 画像を削除チェックボックスをオンにして更新すると、画像が削除される。

image.png

image.png


まとめ


  • 画像ファイルのアップロードはprofile_picturenew_profile_pictureremove_profile_pictureの3つの項目を使うことで、添付と削除が行えることがわかった。

参考

改訂4版 基礎 Ruby on Rails (IMPRESS KISO SERIES)