[Rails]ActiveStorageを使ってアバター機能を実装する方法

  • このエントリーをはてなブックマークに追加

スポンサーリンク

はじめに

ActiveStorageは、ファイルアップロード機能をシンプルに実装できます。

これを利用して、ユーザーが自身のアバター画像をアップロード・表示できる機能を実現することが可能です。

アバター機能はユーザーの個性を表現する重要な要素であり、アプリケーションにおけるUXを高めることができます。

今回は、ActiveStorageを使ってRailsアプリケーションにアバター機能を実装する方法について紹介します。

基本的な設定から、アップロードされた画像を表示するまでの流れを順を追って解説します。

前提条件

Railsアプリケーションの初期設定やDeviseの導入ができているboilerplate(ボイラープレート)で進めます。

もし手元にサンプルアプリケーションがない方は、以下の記事を参考にしてみてください。

アバター機能の要件

ここでは、アバター機能の要件は以下のようにしたいと思います。

  1. ログインしているユーザーのみアバターを変更できる
  2. アバターを設定していない場合は、デフォルト画像を表示する
  3. アバターはページによって異なるサイズで表示できるようにする

それぞれの実装のポイントを示します。

1に関しては、Deviseを導入していれば特に問題ないと思います。

2に関しては、ActiveStorageの attached? を使ってアバター画像を持っているかで分岐する必要があります。デフォルト画像にはRailsチュートリアルで紹介されているGravatarを使います。

3に関しては、ActiveStorageの variant を使ってサイズ違いの画像を作成する必要があります。

ActiveStorageのセットアップ

既にActiveStorageの設定が完了している場合はスキップできます。

ActiveStorageのセットアップ方法については、Railsガイドを参考に進めます。

https://railsguides.jp/active_storage_overview.html

libvipsをインストール

libvipsは画像解析や画像変形用のソフトウェアです。

Macの場合は以下のコマンドでインストールします。

$ brew install vips

image_processingをインストール

画像分析や画像加工のために image_processing gemも必要です。

Gemfileのimage_processing gemをコメント解除するか、必要に応じて追加します。

gem "image_processing", "~> 1.2"

Gemfileを変更したら bundle install を実行しましょう。

$ bundle install

ActiveStorageをインストール

インストールコマンドとマイグレーションコマンドを実行します。

$ bin/rails active_storage:install
$ bin/rails db:migrate

もしクライアントからダイレクトアップロード機能を使いたい場合は、別途npmパッケージなどのインストールが必要です。

この記事では扱わないので、必要であればこちらを参考にしてください。

development環境のディスク設定

config/environments/development.rbに以下の記述があるか確認します。無ければ追加してください。

config.active_storage.service = :local

もし本番環境の設定をしたい場合は、AWS S3などのクラウドストレージを使う方法が一般的です。

この記事では扱わないので、必要であればこちらを参考にしてください。

以上でセットアップは完了です。

アバター画像を設定できるようにする

Userモデルに has_one_attached の記述を追加します。

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
    :recoverable, :rememberable, :validatable

  has_one_attached :avatar

  validates :first_name, :last_name, presence: true
end

続いて、プロフィール更新でアバター画像をアップロードできるようにします。

Viewファイルを変更します。

<h2>Edit <%= resource_name.to_s.humanize %></h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>

  <div class="field">
    <%= f.label :avatar %><br>
    <%= f.file_field :avatar, accept: "image/*" %>
  </div>

  <div class="actions">
    <%= f.submit "Update" %>
  </div>
<% end %>

ここではdeviseのViewファイルを変更していますが、もし独自のファイルを変更したい場合は同じようにfile_fieldを追加してください。

続いて、コントローラーがavatarをパラメーターとして受け取れるように設定します。

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name])
    devise_parameter_sanitizer.permit(:account_update, keys: [:first_name, :last_name, :avatar])
  end
end

Viewと同様にdeviseの設定を変更していますが、もし独自のコントローラーを使いたい場合はストロングパラメーターに追加してください。

ここまでで、フォームでアバター画像を送信してエラーが出ないことを確認してください。

アバター画像を表示する

アバター画像を表示するには、 image_tag を使います。

<%= image_tag current_user.avatar, size: 24, class: "rounded" %>

ここではBootstrapのクラスを使っていますが、見た目は必要に応じて調整してください。

ヘッダーメニューに記載すると以下のよう見た目になります。

一部省略していますが、コードは以下のようになります。

      <ul class="navbar-nav">
        <li class="nav-item"><%= link_to "News", announcements_path, class: "nav-link #{unread_announcements(current_user)}" %></li>

        <% if user_signed_in? %>
          <li class="nav-item dropdown">
            <%= link_to "#", class: "nav-link dropdown-toggle", data: { bs_toggle: "dropdown" }, aria: { expanded: false } do %>
              <%= image_tag current_user.avatar, size: 24, class: "rounded" %>
            <% end %>
            <div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbar-dropdown">
              <%= link_to "Settings", edit_user_registration_path, class: "dropdown-item" %>
              <%= link_to "Password", account_password_path, class: "dropdown-item" %>
              <div class="dropdown-divider"></div>
              <%= button_to "Logout", destroy_user_session_path, method: :delete, class: "dropdown-item" %>
            </div>
          </li>
        <% else %>
          <li class="nav-item"><%= link_to "Sign Up", new_user_registration_path, class: "nav-link" %></li>
          <li class="nav-item"><%= link_to "Login", new_user_session_path, class: "nav-link" %></li>
        <% end %>
      </ul>

デフォルト画像を表示する

アバター画像が設定されるかどうかは、 attached? を使うと判別できます。

if文を使って、アバター画像がある場合はそのまま表示、無ければデフォルト画像を表示するといった感じですね。

ただし、アバター画像はアプリケーションの色々なページで表示する可能性があるので毎回if文を書いていくのはコードの保守性が下がります。

そこでヘルパーを定義して使い回しができるようにします。

ここではAvatarHelperという名前でファイルを作成しています。

module AvatarHelper
  def avatar_url_for(user)
    if user.avatar.attached?
      user.avatar
    else
      gravatar_id = Digest::MD5.hexdigest(user.email.downcase)
      "https://secure.gravatar.com/avatar/#{gravatar_id}"
    end
  end
end

あとは定義した avatar_url_for メソッドを使うように先ほどのViewファイルを変更します。

<%= image_tag avatar_url_for(current_user), size: 24, class: "rounded" %>

これで以下のようにアバター画像がない場合はGravatarの画像が表示されるようになります。

異なるサイズで表示できるようにする

ここまでも基本的な機能は満たしてそうですが、ユーザーがサイズの大きい画像をアップロードした場合を考慮してリサイズした方が良いです。

また、縦や横に長い画像がアップロードされてもアバターとして表示できるように対応します。

AvatarHelperを以下のように変更します。

module AvatarHelper
  def avatar_url_for(user, options = {})
    size = options[:size] || 48
    default_image = options[:default] || "mp"

    if user.avatar.attached?
      user.avatar.variant(resize_to_fill: [size, size])
    else
      gravatar_id = Digest::MD5.hexdigest(user.email.downcase)
      "https://secure.gravatar.com/avatar/#{gravatar_id}?size=#{size}&default=#{default_image}"
    end
  end
end

変更点について解説します。

画像のサイズを引数で受け取れるようにしています。

options = {} のように書くと、呼び出し側で指定された値をハッシュでアクセスできるようになります。

sizeの他にdefaultという指定もできるようにしており、これはGravatarが生成するデフォルト画像の種類を指定できます。指定しなければ mp となっており、人型の画像になります。

user.avatar.variant(resize_to_fill: [size, size]) は画像の加工をしています。

アバター画像なので真ん中を中心にして、指定されたサイズに切り抜くように加工します。

これで完成です。

アバター画像の活用シーン

アバター画像はアプリケーションの色々なページで表示することがあります。

この記事ではヘッダーに表示していましたが、例えば以下のようなUIを作りたい場合でもAvatarHelperのメソッドを呼び出してあげれば可能です。

参考までにコードは以下のようになります。avatar_url_for以外の部分は適宜調整してください。

<% @users.each do |user| %>
  <div class="d-flex">
    <%= image_tag avatar_url_for(user), size: 40, class: "rounded flex-shrink-0 me-2" %>
    <p>
      <strong class="d-block text-secondary">@<%= user.first_name + user.last_name %></strong>
      <%= user.email %>
    </p>
  </div>
<% end %>

まとめ

ActiveStorageを使ってアバター機能を実装しました。

アバター機能があるだけで、グッとアプリケーションが良い感じになると思います。

ActiveStorageのセットアップから画像の表示までを理解することで、より高度なカスタマイズや機能追加にも挑戦できるようになります。

この記事のコードを参考に、自分のアプリケーションに合わせてカスタマイズしてみてください。

参考までに、今回のコミットログはこちらになります。

スポンサーリンク

  • このエントリーをはてなブックマークに追加