Ruby on Rails の 5.2 リリースが近づいてきました。
(この記事執筆時点では `5.2.0.rc1` がリリースされています。)
今回は、Rails 5.2 の目玉の1つである Active Strage を使ってみようと思います。
Active Strage は、ファイルのアップロード機能であり、今まで CarrierWave だったり Shrine を使っていたところです。
Rails の標準機能として実装されるのは嬉しいです。
Active Storage の概要 | Rails ガイド
今回はガイドページにあるように、ユーザーにアバター画像を紐付けるケースを想定して実施していこうと思います。
今回はユーザー1レコードにつき、1つのアバター画像をアタッチします。
そのため、あらかじめユーザーのCRUDを作成しておきます。
今回は分かりやすいよう、 users
テーブルには最低限のカラムしか持たせていません。
(後述しますが、Active Strage用にカラムは必要ないようです。)
$ rails g scaffold User name:string
Active Strageでは、以下の2つのテーブルを使用してファイルをアタッチします。
そのため、まずはこのテーブルを作成するマイグレーションファイルを作成します。
幸いなことにコマンドで生成できます。
$ 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なども指定できます。
ここで宣言したキーを使って( test
や local
など)、各環境での設定ファイルにて指定します。
# 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
のレコードを作成しつつ、 blobs
と attachments
にレコードを作成します。
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_blobs
と active_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: #>
@user.avatar.attached?
でアタッチ済みか判定されます。
アタッチされた画像は image_tag
を使用して下記のように表示できるようです。
<%= image_tag user.avatar %>
また、mini-magickをインストールすれば、 variant
でリサイズを作成することもできます。
<%= image_tag user.image.variant(resize: '100x100') %>
routes.rb
でRoutingErrorのハンドリングをしていると、Active Storage経由の画像が表示されないかも。
こちらのQiita記事にて解説がされておりますので注意です。
active storageの画像がリンク切れする – Qiita
ImageMagick自体をインストールしないと、S3にアップしたファイルのリサイズに失敗します。mini_magick
や aws-sdk-s3
をインストールして満足しないようにしよう。
$ yum -y install ImageMagick
AWSのS3も試してみましたが、コンフィグの書き換えだけで作動しました。
簡単に画像アップロードが用意できるため、今後使われていくのではないでしょうか?