はじめに
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用にカラムは必要ないようです。)
1 | $ rails g scaffold User name:string |
マイグレーションファイル作成
Active Strageでは、以下の2つのテーブルを使用してファイルをアタッチします。
- active_strage_blobs
- active_strage_attachments
そのため、まずはこのテーブルを作成するマイグレーションファイルを作成します。
幸いなことにコマンドで生成できます。
1 2 3 4 5 6 7 8 9 10 | $ 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) =============== |
テーブルのスキーマを見ると、このような感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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ファイルができていました。
こちらでパスなどを設定します。
1 2 3 4 5 6 7 | test: service: Disk root: <%= Rails.root.join("tmp/storage") %> local: service: Disk root: <%= Rails.root.join("storage") %> |
なお、AWS S3やMicrosoft Azureなども指定できます。
ここで宣言したキーを使って(
test
や
local
など)、各環境での設定ファイルにて指定します。
1 2 | # Store uploaded files on the local file system (see config/storage.yml for options) config.active_storage.service = :local |
1 2 | # Store uploaded files on the local file system (see config/storage.yml for options) config.active_storage.service = :test |
実際に使って見る
モデル
まずは、
User
モデルに、アバター画像をアタッチするアクセサを定義します。
1 2 3 4 5 | class User < ApplicationRecord has_one_attached :avatar end |
このアクセサに、Active Strageがファイルをアタッチしてくれます。
なお、レコードとファイルが1対1の場合は
has_one_attached
ですが、1対多の場合は
has_many_attached
になります。
(1対多の関係については後日記事にできたらいいな。)
コントローラー
create
する場合は、ストロングパラメータに含めてしまえば、
User
のレコードを作成しつつ、
blobs
と
attachments
にレコードを作成します。
1 2 3 4 5 6 7 8 9 | 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
を使うようです。
私は下記のように書いてみました。
(もうちょっと良い書き方ありそう。。。)
1 2 3 4 5 6 | def update if @user.update(user_params) && @user.avatar.attach(params[:avatar]) redirect_to user_path(@user) else render :edit end |
画像は、
storage
ディレクトリに暗号化された状態で格納されます。
また、対象の画像の
active_storage_blobs
と
active_storage_attachments
は下記のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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
をはじめ複数のジョブが動きます。
1 | [ActiveJob] Enqueued ActiveStorage::PurgeJob (Job ID: c314d6f1-24e5-4540-b9ba-08b14a0a1f15) to Async(default) with arguments: #> |
ビュー
@user.avatar.attached?
でアタッチ済みか判定されます。
アタッチされた画像は
image_tag
を使用して下記のように表示できるようです。
1 | <%= image_tag user.avatar %> |
また、mini-magickをインストールすれば、
variant
でリサイズを作成することもできます。
1 | <%= image_tag user.image.variant(resize: '100x100') %> |
個人的メモ
RoutingErrorのハンドリング
routes.rb
でRoutingErrorのハンドリングをしていると、Active Storage経由の画像が表示されないかも。
こちらのQiita記事にて解説がされておりますので注意です。
active storageの画像がリンク切れする – Qiita
ImageMagickを忘れずにインストール
ImageMagick自体をインストールしないと、S3にアップしたファイルのリサイズに失敗します。
mini_magick
や
aws-sdk-s3
をインストールして満足しないようにしよう。
1 | $ yum -y install ImageMagick |
さいごに
AWSのS3も試してみましたが、コンフィグの書き換えだけで作動しました。
簡単に画像アップロードが用意できるため、今後使われていくのではないでしょうか?