Help us understand the problem. What is going on with this article?

基礎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)

tseno
Java、Kotlinのフリーランスエンジニア
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away