カテゴリー: BackEnd

Active Strageを使用してユーザーのアバターを登録、表示する

はじめに

Ruby on Rails の 5.2 リリースが近づいてきました。
(この記事執筆時点では `5.2.0.rc1` がリリースされています。)

今回は、Rails 5.2 の目玉の1つである Active Strage を使ってみようと思います。
Active Strage は、ファイルのアップロード機能であり、今まで CarrierWave だったり Shrine を使っていたところです。
Rails の標準機能として実装されるのは嬉しいです。
Active Storage の概要 | Rails ガイド

今回はガイドページにあるように、ユーザーにアバター画像を紐付けるケースを想定して実施していこうと思います。

環境

  • Ruby 2.5.0
  • Rails 5.2.0.rc1

セットアップ

前準備

今回はユーザー1レコードにつき、1つのアバター画像をアタッチします。
そのため、あらかじめユーザーのCRUDを作成しておきます。
今回は分かりやすいよう、 users テーブルには最低限のカラムしか持たせていません。
(後述しますが、Active Strage用にカラムは必要ないようです。)

$ rails g scaffold User name:string

マイグレーションファイル作成

Active Strageでは、以下の2つのテーブルを使用してファイルをアタッチします。

  • active_strage_blobs
  • active_strage_attachments

そのため、まずはこのテーブルを作成するマイグレーションファイルを作成します。
幸いなことにコマンドで生成できます。

$ rails active_storage:install:migrations
Copied migration 20180304111746_create_active_storage_tables.active_storage.rb from active_storage

$ rails db:migrate
== 20180304111746 CreateActiveStorageTables: migrating ========================
-- create_table(:active_storage_blobs)
   -> 0.0397s
-- create_table(:active_storage_attachments)
   -> 0.0294s
== 20180304111746 CreateActiveStorageTables: migrated (0.0699s) ===============

テーブルのスキーマを見ると、このような感じです。

create_table "active_storage_attachments", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.string "name", null: false
    t.string "record_type", null: false
    t.bigint "record_id", null: false
    t.bigint "blob_id", null: false
    t.datetime "created_at", null: false
    t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
    t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
  end

  create_table "active_storage_blobs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.string "key", null: false
    t.string "filename", null: false
    t.string "content_type"
    t.text "metadata"
    t.bigint "byte_size", null: false
    t.string "checksum", null: false
    t.datetime "created_at", null: false
    t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
  end

blobs の方が、アタッチしたファイル自体を管理しています。
attachments の方で、どのレコードと blobs が紐づいているか管理しています。

設定ファイル

Rails 5.2 では、Active Strage用のYAMLファイルができていました。
こちらでパスなどを設定します。

test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

なお、AWS S3やMicrosoft Azureなども指定できます。
ここで宣言したキーを使って( testlocal など)、各環境での設定ファイルにて指定します。

# Store uploaded files on the local file system (see config/storage.yml for options)
config.active_storage.service = :local
# Store uploaded files on the local file system (see config/storage.yml for options)
config.active_storage.service = :test

実際に使って見る

モデル

まずは、 User モデルに、アバター画像をアタッチするアクセサを定義します。

class User < ApplicationRecord
 
  has_one_attached :avatar

end

このアクセサに、Active Strageがファイルをアタッチしてくれます。
なお、レコードとファイルが1対1の場合は has_one_attached ですが、1対多の場合は has_many_attached になります。
(1対多の関係については後日記事にできたらいいな。)

コントローラー

create する場合は、ストロングパラメータに含めてしまえば、User のレコードを作成しつつ、 blobsattachments にレコードを作成します。

  def create
    user = User.create!(user_params)
    redirect_to root_path
  end
 
  private
    def user_params
      params.require(:user).permit(:name, :avatar)
    end

更新の場合は、 avatar.attach を使うようです。
私は下記のように書いてみました。
(もうちょっと良い書き方ありそう。。。)

  def update
    if @user.update(user_params) && @user.avatar.attach(params[:avatar])
      redirect_to user_path(@user)
    else
      render :edit
    end

画像は、 storage ディレクトリに暗号化された状態で格納されます。

また、対象の画像の active_storage_blobsactive_storage_attachments は下記のようになっています。

mysql> select * from active_storage_attachments \G;
*************************** 1. row ***************************
         id: 1
       name: avatar
record_type: User
  record_id: 1
    blob_id: 1
 created_at: 2018-03-10 13:49:45
1 row in set (0.00 sec)

mysql> select * from active_storage_blobs  \G;
*************************** 1. row ***************************
          id: 22
         key: JFSewQeCHMAgxknQvfsT1zCZ
    filename: test.jpg
content_type: image/jpeg
    metadata: {"identified":true,"width":700,"height":715,"analyzed":true}
   byte_size: 122227
    checksum: QT1z+xQlvWHbG9HxmIddXA==
  created_at: 2018-03-10 13:49:45
1 row in set (0.00 sec)

また、保存時には ActiveJob をはじめ複数のジョブが動きます。

[ActiveJob] Enqueued ActiveStorage::PurgeJob (Job ID: c314d6f1-24e5-4540-b9ba-08b14a0a1f15) to Async(default) with arguments: #&gt;

ビュー

@user.avatar.attached? でアタッチ済みか判定されます。
アタッチされた画像は image_tag を使用して下記のように表示できるようです。

<%= image_tag user.avatar %>

また、mini-magickをインストールすれば、 variant でリサイズを作成することもできます。

<%= image_tag user.image.variant(resize: '100x100') %>

個人的メモ

RoutingErrorのハンドリング

routes.rb でRoutingErrorのハンドリングをしていると、Active Storage経由の画像が表示されないかも。
こちらのQiita記事にて解説がされておりますので注意です。
active storageの画像がリンク切れする – Qiita

ImageMagickを忘れずにインストール

ImageMagick自体をインストールしないと、S3にアップしたファイルのリサイズに失敗します。
mini_magickaws-sdk-s3 をインストールして満足しないようにしよう。

$ yum -y install ImageMagick

さいごに

AWSのS3も試してみましたが、コンフィグの書き換えだけで作動しました。
簡単に画像アップロードが用意できるため、今後使われていくのではないでしょうか?

naoki85

シェア
執筆者:
naoki85
タグ: Rails

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前