【Rails 8対応】ActiveStorageでアバター機能を実装する方法|画像アップロード・variantリサイズ・Gravatarフォールバック

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

スポンサーリンク

「ユーザーのアイコン(アバター)を表示したいけれど、画像アップロードの仕組みをゼロから作るのは面倒そう」——そう感じたことはありませんか?Railsには標準で Active Storage というファイルアップロード機能が用意されていて、gemを1つ足すだけでアバター機能を実装できます。この記事では、Active Storage を使って「画像をアップロード → リサイズして表示 → 未設定ならデフォルト画像を出す」までを、Rails 8 を前提に順を追って解説します。

Rails のセットアップからやり直したい方は、macOSにRuby on Railsをインストールもあわせてどうぞ。認証まわりで Devise を使うか標準ジェネレータを使うか迷っている方は、Rails 8の認証ジェネレータとDeviseはどっちを使う?も参考になります。

この記事でやること

先に、この記事で実装するアバター機能の要件を整理しておきます。

  1. ログインしているユーザーだけが自分のアバターを変更できる
  2. アバターを設定していない場合は、デフォルト画像(Gravatar)を表示する
  3. ページによって異なるサイズで表示できるようにする(一覧では小さく、詳細では大きく等)

それぞれのポイントはこうです。要件1は Devise を導入していれば current_user で判定できます。要件2は Active Storage の attached? で「画像を持っているか」を判定し、無ければ Gravatar にフォールバックします。要件3は Active Storage の variant でサイズ違いの画像を生成します。

前提

  • Rails 7 または Rails 8 のアプリがある(Active Storage は標準で同梱)
  • Devise などでログイン機能が実装済み(current_user が使える)

この記事では、Devise 導入済みの boilerplate(ボイラープレート)をベースに進めます。手元にサンプルがない方は、boilerplateを作成してGithubのTemplate Repositoryにする方法も参考にしてください。

Active Storageのセットアップ

すでに Active Storage を設定済みの場合はこの章を飛ばして構いません。まだの方は、以下の3ステップでセットアップします。公式の詳細は Active Storage の概要(Railsガイド)を参照してください。

①libvipsをインストールする

画像のリサイズや切り抜きには libvips を使います。Rails 7.0 以降は変換処理のデフォルトが libvips(ruby-vips)になっているため、ImageMagick ではなく vips を入れておくのがおすすめです。Macなら Homebrew でインストールできます。

$ brew install vips

②image_processing gemを有効にする

画像の変換には image_processing gem も必要です。rails new で生成した Gemfile にはコメントアウトされた状態で入っているので、コメントを外すか、無ければ追記します。

gem "image_processing", "~> 1.2"

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

$ bundle install

③Active Storageをインストールする

インストール用のコマンドとマイグレーションを実行します。これで active_storage_blobs などの管理テーブルが作成されます。

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

開発環境ではローカルディスクに保存する設定になっているか、config/environments/development.rb を確認します。無ければ追加してください。本番環境では AWS S3 などのクラウドストレージを使うのが一般的ですが、その設定はRailsガイドのセットアップを参照してください。

config.active_storage.service = :local

⚠️ variant処理のセキュリティ注意(2026年7月)

この記事のように variant(画像変換)を使う場合は、Rails のバージョンに注意してください。2026年7月29日、Active Storage の variant 処理に、細工した画像をアップロードすることでサーバー上の任意のファイルを読み取られる/リモートコード実行につながる脆弱性 CVE-2026-66066 が公表されました。デフォルトの libvips 構成が影響を受けます。

修正版は Rails 7.2.3.2 / 8.0.5.1 / 8.1.3.1 以降です。ユーザーがアップロードした画像を variant で変換するアプリでは、必ず修正版へアップデートしておきましょう。

$ bundle update rails
$ bundle list | grep rails   # 7.2.3.2 / 8.0.5.1 / 8.1.3.1 以降になっているか確認

Userモデルにアバターを持たせる

まず User モデルに has_one_attached を追加します。これで user.avatar という形でアバター画像を1枚ひも付けられるようになります。

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

フォームで画像をアップロードできるようにする

次に、プロフィール更新画面からアバターをアップロードできるようにします。Devise の登録編集ビューにファイル選択欄(file_field)を追加します。

<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 %>

accept: "image/*" を付けると、ファイル選択のダイアログで画像だけが選ばれやすくなります。ここでは Devise のビューを直接編集していますが、独自のプロフィール編集画面を使っている場合も同じように file_field :avatar を追加すればOKです。Devise のフォームを分割してカスタマイズしたい場合は、Deviseをカスタマイズして、プロフィール更新とパスワード変更を分ける方法も参考になります。

続いて、コントローラーが avatar パラメーターを受け取れるように、ストロングパラメーターに追加します。Devise の場合は configure_permitted_parameters で許可します。

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

独自コントローラーの場合は、通常の params.require(:user).permit(..., :avatar) のように :avatar を許可すれば同じことができます。ここまでで、フォームから画像を送信してエラーが出ないことを確認しましょう。

アバター画像を表示する

アップロードした画像は image_tag で表示できます。Active Storage のアタッチメントをそのまま渡せます。

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

ここでは Bootstrap のクラス(rounded)を使っていますが、見た目は自由に調整してください。Tailwind CSS でフォームや UI を整えたい場合は、Tailwind CSSでフォームを綺麗に作る方法もあわせてどうぞ。ヘッダーのユーザーメニューに置くと、次のような見た目になります。

Railsアプリのヘッダーにアバター画像を表示した例

ヘッダー部分のコードは、一部省略すると以下のようになります。

      <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>

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

アバターを設定していないユーザーには、デフォルト画像を出したいところです。画像を持っているかは attached? で判定できます。未設定なら Gravatar の画像にフォールバックさせましょう。

ただし、毎回ビューで if 文を書くと保守性が下がります。そこでヘルパーにまとめて使い回せるようにします。ここでは AvatarHelper を作成します。

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

あとは、ビューで avatar_url_for を呼び出すように変更します。

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

これで、アバターが未設定のときは Gravatar の画像が表示されます。

アバター未設定時にGravatarのデフォルト画像を表示した例

【2026年時点の注意】Gravatarのハッシュは MD5 → SHA-256 に変わりました。以前は Digest::MD5 でメールアドレスをハッシュ化するのが定番でしたが、Gravatar は2024年に SHA-256 対応を開始し、2025年にはアバターURLのハッシュを SHA-256 へ切り替えました。MD5 はメールアドレスを逆算されやすくプライバシー面でも弱いため、これから実装するなら Digest::SHA256 を使ってください。古い記事の Digest::MD5.hexdigest(...) をそのままコピペすると、Gravatar 画像が正しく表示されない場合があります。ハッシュ化の前に strip.downcase で前後の空白を除去して小文字化するのも忘れずに。

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

ここまでで基本機能は満たしていますが、ユーザーが大きすぎる画像や縦横比の偏った画像をアップロードすることを考えると、表示前にリサイズ・切り抜きをしておくと安心です。Active Storage の variant を使うと、元画像を加工したバリエーションを生成できます。ヘルパーを次のように拡張します。

module AvatarHelper
  def avatar_url_for(user, size: 48, default: "mp")
    if user.avatar.attached?
      user.avatar.variant(resize_to_fill: [size, size])
    else
      hash = Digest::SHA256.hexdigest(user.email.strip.downcase)
      "https://gravatar.com/avatar/#{hash}?size=#{size}&default=#{default}"
    end
  end
end

変更点を解説します。まず、表示サイズをキーワード引数 size: で受け取れるようにしました。呼び出し側で avatar_url_for(user, size: 40) のように指定できます。

user.avatar.variant(resize_to_fill: [size, size]) が画像加工の本体です。resize_to_fill は、指定したサイズに収まるよう中央を基準に切り抜く(余白を作らない)変換なので、アバターのような正方形表示にぴったりです。Gravatar 側にも size(画像サイズ)と default(デフォルト画像の種類。mp は人型のシルエット)を渡して、サイズ感をそろえています。

Gravatarのdefaultパラメーターで選べるデフォルト画像の種類

いろいろなページで使い回す

アバターは、ヘッダーだけでなくユーザー一覧やコメント欄など、さまざまな場所で表示します。ヘルパーにまとめておけば、どこでも avatar_url_for を呼ぶだけで済みます。例えば、次のようなユーザー一覧のUIも簡単に作れます。

アバター付きのユーザー一覧UIの例

参考までに、上のUIのコードは以下のようになります。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 %>

まとめ

Active Storage を使って、アバターのアップロード・表示・リサイズ・デフォルト画像までを実装しました。ポイントを振り返ると、次のとおりです。

  • アップロードは has_one_attached :avatar + ストロングパラメーターで受け取る
  • 未設定時のフォールバックは attached? + Gravatar(ハッシュは SHA-256)
  • サイズ違いは variant(resize_to_fill: ...) でリサイズ・切り抜き
  • variant を使うなら CVE-2026-66066 対策として Rails を修正版へ更新する

アバター機能があるだけで、アプリはぐっと「それっぽく」なります。Active Storage の基本を押さえておくと、投稿画像やサムネイルなど他の機能にも応用が利きます。Rails の実装パターンをもっと知りたい方は、rails ブログ機能の実装方法7選もあわせてどうぞ。

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

スポンサーリンク

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

コメント

Comments are closed.