Rails

【Rails6.0】Active Storageを用いた単数枚、複数枚画像投稿の実装手順をそれぞれ解説

Rails

 

今回はActive Storageを用いた単数枚画像投稿、複数枚画像投稿の実装方法をそれぞれ解説したいと思います。ファイルアップロード機能を扱うにはCarrierWaveやShrineといったgemの使用が定番でしたが、今後はActive Storageが主流になってくるので、この機会にぜひマスターしておきましょう。

2020/7/1時点で最新バージョンであるRails6.0で実装、解説しています。

Active Storageとは

Image from Gyazo

 

Active Storageとは、Rails5.2から追加されたファイルアップロード機能です。

Amazon S3、Google Cloud Storage、Microsoft Azure Storageなどのクラウドストレージサービスへのファイルアップロードも担ってくれます。

これまでRailsでファイルアップロード機能を実装する際はCarrierWaveやShrineといったgemを使用することが定番でした。しかしRails5.2からは、Railsが公式にファイルアップロード機能を提供しました。

それがActive Storageです。

Q&A

クリックすることでAnswerを見ることができます。
Active Storageのメリットは?

Active Storageを用いることによるメリットは主に以下の4つです。

・Railsが公式に提供している機能のため安全

・Rails6.0から追加された新機能(Action TextやAction Mailbox)に対応

・クラウドストレージ(S3やGoogle Cloudなど)にダイレクトアップロードが可能

・画像複数枚投稿の実装が比較的簡単

反対にデメリットはあるの?

結論あります。今後普及してくることは間違いないですが、まだ出てきたばかりの機能なのでCarrierWaveやShrineと比べてやや劣る部分が見受けられます。

Active Storageの大きなデメリットとしては以下の2つです。

・バリデーション機能の不足

ファイルアップロード機能にはバリデーションが不可欠ですが、Rails6.0のActive Storageにはその機能は梱包されていません。自分で実装するか、Active Storage Validationsというgemを使う必要があります。

本当はRails6.0でバリデーション機能が実装される予定でしたが、時間切れでマージに至らずでした。そのため、Rails6.1ではおそらく導入されるでしょう。

・Cacheの不足

バリデーションに引っ掛かってしまい投稿をやり直す場合、Active Storageは全てまた打ち込み直すという作業が発生してしまいます。

しかし、他のライブラリではcacheという機能を用いてそういった手間を省略することができるため、これはActive Storageの大きなデメリットと言わざるを得ません。

これからファイルアップロード機能を実装する場合はActive Storageを使えばいいの?

どんな場合でもActive Storageが良いということはありません。上記に記したデメリットを加味しながら実装していく必要があるため、時と場合によって使い分けましょう。

ただ今後はActive Storageが主流になってくると思われます。

覚えておくべき概念

実装に移る前に、Active Storageの覚えておくべき概念を予習しておきましょう。

Active Storageは、データベースで「active_storage_blobs」と「active_storage_attachments」という名前の2つのテーブルを使用します。

  • active_storage_blobs:アップロードファイルのメタ情報を管理するモデル
  • active_storage_attachments:該当モデルとblobsの中間テーブルに該当するモデル

 

どのモデルに紐付けられた画像もこの2つのテーブルを利用します。これはポリモーフィック関連を選択、利用していることになります。

 

もう少し噛み砕いて見ていきましょう。

この2つのテーブルと該当モデルとの関係を図で表すと以下のようになります。UserモデルとPostモデルが該当モデルと仮定すると、どちらも共通のAttachmentとBlobを使用していることになりますね。

単数枚画像投稿の場合
複数枚画像投稿の場合
他の画像アップロード用のgemでは、対象のモデルに画像を扱うカラムを追加していたね。

実装方法

では実際にActive Storageを用いた実装を行ってみましょう。今回は、単数画像を投稿するアプリケーションと複数画像を投稿できるアプリケーションの2つを作成していきます。

単数枚画像投稿
Image from Gyazo
複数枚画像投稿
Image from Gyazo

単数枚画像の場合

まずはアプリケーションの導入を行いましょう。以下のコマンドを打ち込み、新規アプリケーションを立ち上げます。今回はタイトルにもある通り、Rails6.0で開発していきます。

% rails _6.0.0_ new single-image-app -d mysql

今回開発するアプリケーションの環境は以下のようになります。

  • アプリケーション名:single-image-app
  • Rails:6.0.0
  • Ruby:2.6.5
  • DB:MySQL
find_spec_for_exe': can't find gem railties (= 6.0.0) with executable rails (Gem::GemNotFoundException)

このようなエラーが出た場合は、Rails6.0.0がインストールされていない可能性があります。

以下のコマンドでRails6.0.0をインストールしましょう。

% gem install rails -v 6.0.0

 

インストール後「$ gem list rails」でRails6.0.0が入っていれば完了です。

Rails6.0.0インストール

 

次にActive Storageを導入する作業に入ります。以下のコマンドを打ち込むだけで、Active Storageを使うために必要なマイグレーションファイルが生成されます。

このマイグレーションファイルが前述した、「active_storage_blobs」と「active_storage_attachments」になります。

$ cd active-storage-app
$ rails active_storage:install
マイグレーションファイルの生成

 

続いてScaffoldの機能を用いてUser関連のファイルを一括生成しましょう。カラムはnameのみとします。

% rails g scaffold user name portrait:attachment
「portrait:attachment」を付けると、Userモデルに「has_one _attached :portrait」が自動的に追加されます。
Rails
【Rails】秒速でアプリケーション開発できるscaffoldの使い方を簡単にまとめてみた手っ取り早くアプリケーションを開発することができるscaffold(スキャフォールド)の使い方について簡単に解説しています。新しい機能実装をテストしてみたい、すぐにでもアプリケーションを立ち上げたいといった場合に便利な機能なので、ぜひ覚えておきましょう。...

 

最後にデータベースを作成するコマンドとマイグレートを実行すれば、単数枚画像投稿機能が完成です。

% rails db:create
% rails db:migrate

 

では実際にサーバーを立ち上げ「localhost:3000/users」にアクセスし、挙動を確認しましょう。

Image from Gyazo

 

しかし今のままではファイルのアップロードができても、プレビュー表示ができていませんね。では次にプレビュー機能の実装に取り組みましょう。

プレビュー表示にはImageProcessing(MiniMagickでも可)とImageMagickが必要になるため、事前にインストールしておく必要があります。

↓ Gemfile

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

↓ コメントアウトを外します

# Use Active Storage variant
gem 'image_processing', '~> 1.2'
% bundle install
% brew install imagemagick

 

インストール完了後、「app/views/users/show.html.erb」のリンクの箇所を以下のように変更しましょう。

↓ app/views/users/show.html.erb

<p>
  <strong>Portrait:</strong>
  <%= link_to @user.portrait.filename, @user.portrait if @user.portrait.attached? %>
</p>

↓以下のように変更

<p>
  <strong>Portrait:</strong>
  <%= image_tag @user.portrait.variant(resize_to_limit: [100, 100]) %>
</p>

variantメソッドは画像の変換、加工処理をしてくれるメソッドです。ImageProcessingのメソッドが存在していればImageProcessing経由で、ImageProcessingメソッドが存在していなければMiniMagick経由で画像処理されます。

今回はImageProcessingを導入しているので、ImageProcessing経由の画像処理になります。

 

では画像がきちんとプレビュー表示されるか確認しましょう。

Image from Gyazo

複数枚画像の場合

では続いて、複数枚画像投稿ができるアプリケーションを作成していきましょう。

単数枚画像を実装したアプリケーションをそのまま引用し、複数枚画像投稿ができるアプリケーションにカスタムしていきます。

変更箇所は以下の4つです。

  • user.rb
  • _form.html.erb
  • users_controller.rb
  • show.html.erb

 

では実際に変更箇所を修正していきます。

まずはuser.rbを変更しましょう。複数画像を投稿したい場合は、UserモデルとBlobを1対多の関係で関連付ける必要があります。その役割を担うのが「has_many_attached :portraits」です。

has_one_attached :portrait

↓以下のように変更

has_many_attached :portraits
複数枚画像投稿の場合

 

続いては「_form.html.erb」を編集しましょう。複数枚画像投稿の場合は「multiple: true」を追記することによって、複数枚画像を選択することができます。

<div class="field">
  <%= form.label :portrait %>
  <%= form.file_field :portrait, direct_upload: true %>
</div>

↓以下のように変更

<div class="field">
  <%= form.label :portrait %>
  <%= form.file_field :portraits, direct_upload: true, multiple: true %>
</div>

 

次は「users_controller.rb」の編集です。送られてくるパラメーターは配列に格納されているため、以下のようにストロングパラメーターも配列形式にする必要があります。

def user_params
  params.require(:user).permit(:name, :portrait)
end

↓以下のように変更

def user_params
  params.require(:user).permit(:name, portraits: [])
end

binding.pryを使用し、送られてくるパラメーターを確認すると以下のようになっています。配列の中に文字列に変換された3枚の画像データが入っていることが分かりますね。

 pry(#<UsersController>)> params
=> <ActionController::Parameters {"authenticity_token"=>"ew3g6zPkOMHo0+ZadhdLaq9lbLJYOAT83S9SAUSajZC2ouEIsfymBkvE0eUOMF627OSP5RslVZu3qksgwPP1WQ==", "user"=>{"name"=>"テスト画像", "portraits"=>["eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBFUT09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--7089fb75dc00ab071a3211a89b2fffdbbe5b4d38", "eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBFZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--62af32587069d480ecfa2cfb694b2a3eb604e7dd", "eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBFdz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--d105feb18bcca0bce9899581376fd0d57133b3c9"]}, "commit"=>"Create User", "controller"=>"users", "action"=>"create"} permitted: false>

 

最後に「show.html.erb」を変更しましょう。画像は複数枚表示されるため、each文を使って書き換える必要があります。

<p>
  <strong>Portrait:</strong>
  <%= image_tag @user.portrait.variant(resize_to_limit: [100, 100]) %>
</p>

↓以下のように変更

<p>
  <strong>Portrait:</strong>
  <% if @user.portraits.attached? %>
    <% @user.portraits.each do |portrait| %>
      <%= image_tag portrait.variant(resize_to_limit: [100, 100]) %>
    <% end %>
  <% end %>
</p>

「@user.portraits.attached?」で特定のユーザーが画像を持っているか調べることができます。

 

では画像がきちんとプレビュー表示されるか確認しましょう。

Image from Gyazo
単数枚、複数枚投稿の違いまとめ

単数枚画像投稿の場合は、以下を記述する。

  • has_one_attached :portrait
  • params.require(:user).permit(:name, :portrait)

一方で複数枚画像投稿の場合は、以下を記述する。

  • has_many_attached :portraits
  • multiple: true
  • params.require(:user).permit(:name, portrait: [])

まとめ

  • Active Storageとは、Rails5.2から追加されたファイルアップロード機能のこと
  • Active Storageには多くのメリットがある一方で、バリデーションとCacheの不足というデメリットがある
  • 「active_storage_blobs」と「active_storage_attachments」という名前の2つのテーブルを使用する

参考

Railsガイド
https://railsguides.jp/active_storage_overview.html

CarrierWave
https://github.com/carrierwaveuploader/carrierwave

Shrine
https://shrinerb.com/

ポリモーフィック関連
https://qiita.com/kamohicokamo/items/c13f72d720040cfd796d

 

 

今回はActive Storageを用いた単数枚画像投稿、複数枚画像投稿の実装方法をそれぞれ解説しました。バリデーションやCacheが不足しているというデメリットはありますが、ご覧の通り、実装が簡単でしたよね。今後はActive Storageが主流になってくると思うので、ぜひこの機会に実装方法を覚えていただけたらと思います。

Rails6.1に期待だね。