Ruby on Rails:ActiveStorage で画像アップロード

Active Strorage 概要

Active Storageはクラウドストレージサービス(Amazon S3, Google Cloud Storage等)へのファイルアップロードや、ファイルを Active Record オブジェクトにアタッチする機能を提供する。

その他にも、アプリケーションにアップロードした画像の変形や、PDFや動画等の画像以外のアップロードファイルの内容を画像にしたり、任意のファイルからメタデータ抽出にも利用できる。

Active Storage の環境構築

Active Storage の多くの機能は、サードパーティソフトウェアに依存しているため、別途インストールする。インストール対象を下記にまとめる。

ライブラリ名 概要 備考
libvips 画像解析や画像変形用 v8.6以降
FFmpeg 動画や音声の解析や動画プレビュー用 v3.4以降
Poppler PDFプレビュー用 -

それぞれインストールしていく。

  • libvips

    brew install vips
    
  • ffmpeg

    brew install ffmpeg
    
  • ffmpeg

    brew install poppler
    

次に画像分析、画像加工のために image_processing gem も必要ので Gemfile のコメントを外すか、下記を追加する。

gem "image_processing", ">= 1.2"

Active Storage のセットアップ

Active Storage では3つのテーブルを使用する。

  • active_storage_blobs
  • active_storage_variant_records
  • active_storage_attachments

上記を作るために、下記コマンドを実行する。

% rails active_storage:install

上記コマンドを実行すると、xxx_create_active_storage_tables.active_storage.rb というマイグレーションファイルが作られる。

マイグレーションファイルを実行して、モデルを作るために下記を実行する。

% rails db:migrate

Active Storage のサービスを config/storage.yml で宣言する。サービスを Active Storage を認識させるために、Rails.application.config.active_storage.service を設定する。

上記の設定は、環境ごとに行うことが推奨されているので、 development 環境で使うために、 config/environments/development.rb に下記を追加する。

# ファイルをローカルに保存する
config.active_storage.service = :local

パラメータに許可を与える

まずは、deviseで画像データを扱えるように、アプリケーションコントローラで許可を与える。

復習として、Strong Parameters により、モデルのデータ更新には、許可する属性をコントローラで明示的に指定する必要がある。

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  ...
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    keys = %i[name postal_code address self_introduction avatar]
    devise_parameter_sanitizer.permit(:sign_up, keys: keys)
    devise_parameter_sanitizer.permit(:account_update, keys: keys)
  end
  ...
end

ファイルをレコードに添付する

User モデルに画像を添付するために、User モデルを以下のように定義する。

# app/models/user.rb
class User < ApplicationRecord
  ...
  has_one_attached :avatar
  ...
end

ビューを設定する

画像をアップロードする form メソッドを持つビューには、 <%= form.file_field :avatar %> を追加する。

<!-- app/views/devise/registrations/new.html.erb, edit.html.erb -->
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
...
  <div class="field">
    <%= f.label :avatar %><br />
    <%= f.file_field :avatar %>
  </div>
...

画像を表示するビューには、 <%= image_tag user.avatar if user.avatar.attached? %> を追加する。

<!-- app/views/users/index.html.erb, show.html.erb -->
...
<%= image_tag(user.avatar) if user.avatar.attached? %>
...

avatar.attached? メソッドにて、特定のuserがアバター画像を持っているかどうかを調べて、持っていれば image_tag メソッドに画像を渡す。

上記の条件文がない場合、下記のようなエラーとなる。

Can't resolve image into URL: undefined method `persisted?' for nil:NilClass

パッと調べた感じでは、image_tag メソッドに nilオブジェクト(画像データを持っていないレコード)を渡すとエラーになってしまう。

ここまでしてあげると、下記のように画像を上げられて、一覧画面にも表示されるようになる😄

image.png

variant で画像を変形する

画像の変形には、variant メソッドを用いる。ビューにて下記を実施する。

- <%= image_tag(user.avatar) if user.avatar.attached? %>
+ <%= image_tag user.avatar.variant(resize_to_limit: [100, 100]) if user.avatar.attached? %>

上のコードでは、高さと幅を 100px ✖️ 100px に制限したavatar BLOB のバリアントを作成し、処理する。

参考