はじめに
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 | $railsgscaffold 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 migration20180304111746_create_active_storage_tables.active_storage.rbfrom active_storage $rails db:migrate ==20180304111746CreateActiveStorageTables:migrating======================== --create_table(:active_storage_blobs) ->0.0397s --create_table(:active_storage_attachments) ->0.0294s ==20180304111746CreateActiveStorageTables: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::cascadedo|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::cascadedo|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 | classUser<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 | defcreate user=User.create!(user_params) redirect_toroot_path end private defuser_params params.require(:user).permit(:name,:avatar) end |
更新の場合は、 avatar.attach
を使うようです。
私は下記のように書いてみました。
(もうちょっと良い書き方ありそう。。。)
1 2 3 4 5 6 | defupdate if@user.update(user_params)&&@user.avatar.attach(params[:avatar]) redirect_touser_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*fromactive_storage_attachments\G; ***************************1.row*************************** id:1 name:avatar record_type:User record_id:1 blob_id:1 created_at:2018-03-1013:49:45 1rowinset(0.00sec) mysql>select*fromactive_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-1013:49:45 1rowinset(0.00sec) |
また、保存時には ActiveJob
をはじめ複数のジョブが動きます。
1 | [ActiveJob]Enqueued ActiveStorage::PurgeJob(Job ID:c314d6f1-24e5-4540-b9ba-08b14a0a1f15)toAsync(default)with arguments:#> |
ビュー
@user.avatar.attached?
でアタッチ済みか判定されます。
アタッチされた画像は image_tag
を使用して下記のように表示できるようです。
1 | <%=image_taguser.avatar%> |
また、mini-magickをインストールすれば、 variant
でリサイズを作成することもできます。
1 | <%=image_taguser.image.variant(resize:'100x100')%> |
個人的メモ
RoutingErrorのハンドリング
routes.rb
でRoutingErrorのハンドリングをしていると、Active Storage経由の画像が表示されないかも。
こちらのQiita記事にて解説がされておりますので注意です。
active storageの画像がリンク切れする – Qiita
ImageMagickを忘れずにインストール
ImageMagick自体をインストールしないと、S3にアップしたファイルのリサイズに失敗します。mini_magick
や aws-sdk-s3
をインストールして満足しないようにしよう。
1 | $yum-yinstall ImageMagick |
さいごに
AWSのS3も試してみましたが、コンフィグの書き換えだけで作動しました。
簡単に画像アップロードが用意できるため、今後使われていくのではないでしょうか?