Rails5.2がRC2になっていたのでActiveStorageの挙動を少し調べてみました。
準備
まずActiveStorageを使うためにmigrationファイルを作成します。
bin/rails active_storage:install
bin/rails db:migrate
ActiveStorageではCarrierwaveなどと違いファイルの情報を別テーブルに保存するため、これを実行してActiveStorage用のテーブルを作成します。
テーブルがないと↓のように怒られます。
ActiveRecord::StatementInvalid: Could not find table 'active_storage_attachments'
migrateを実行するとactive_storage_blobs
と active_storage_attachments
というテーブルが作成されます。
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
create_table :active_storage_blobs 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 ], unique: true
end
create_table :active_storage_attachments do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false
t.references :blob, null: false
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
end
end
end
モデルの設定
Userというモデルにavatarという名前でデータをアップロードできる様にします。
has_one_attachedだと一つだけ、has_many_attachedではデータをアップロードできます。
class User < ApplicationRecord
has_one_attached :avatar
end
アップロード
has_xxx_attachedで設定した名前のメソッドがはえるのでそれにattachでアップロードします。
user.avatar.attach(io: File.open(Rails.root.join("./tmp/avatar.jpg")), filename: "avatar.jpg", content_type: "image/jpg")
実行時のログ
先にkeyが作られてJobで後からアップロード処理される様です。
ActiveStorage::Attachment Load (0.1ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ? [["record_id", 1], ["record_type", "User"], ["name", "avatar"], ["LIMIT", 1]]
Disk Storage (4.6ms) Uploaded file to key: dHerwrM2wUCeBdbGv8fBg41W (checksum: qtC9awH9X+Lp5VABpTx0RQ==)
(0.1ms) begin transaction
ActiveStorage::Blob Create (0.4ms) INSERT INTO "active_storage_blobs" ("key", "filename", "content_type", "metadata", "byte_size", "checksum", "created_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["key", "dHerwrM2wUCeBdbGv8fBg41W"], ["filename", "avatar.jpg"], ["content_type", "image/jpeg"], ["metadata", "{\"identified\":true}"], ["byte_size", 85092], ["checksum", "qtC9awH9X+Lp5VABpTx0RQ=="], ["created_at", "2018-03-31 14:08:08.224345"]]
(0.8ms) commit transaction
(0.1ms) begin transaction
ActiveStorage::Attachment Create (0.6ms) INSERT INTO "active_storage_attachments" ("name", "record_type", "record_id", "blob_id", "created_at") VALUES (?, ?, ?, ?, ?) [["name", "avatar"], ["record_type", "User"], ["record_id", 1], ["blob_id", 1], ["created_at", "2018-03-31 14:08:08.249993"]]
User Update (0.2ms) UPDATE "users" SET "updated_at" = ? WHERE "users"."id" = ? [["updated_at", "2018-03-31 14:08:08.252816"], ["id", 1]]
(0.8ms) commit transaction
Enqueued ActiveStorage::AnalyzeJob (Job ID: 15433d76-d866-4d41-b32b-5d2b5e04bf4c) to Async(default) with arguments: #<GlobalID:0x007faf8a1fdc40 @uri=#<URI::GID gid://rails52/ActiveStorage::Blob/1>>
=> nil
ActiveStorage::Blob Load (0.1ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Performing ActiveStorage::AnalyzeJob (Job ID: 15433d76-d866-4d41-b32b-5d2b5e04bf4c) from Async(default) with arguments: #<GlobalID:0x007faf8a26dec8 @uri=#<URI::GID gid://rails52/ActiveStorage::Blob/1>>
Skipping image analysis because the mini_magick gem isn't installed
(0.1ms) begin transaction
ActiveStorage::Blob Update (0.2ms) UPDATE "active_storage_blobs" SET "metadata" = ? WHERE "active_storage_blobs"."id" = ? [["metadata", "{\"identified\":true,\"analyzed\":true}"], ["id", 1]]
(0.8ms) commit transaction
Performed ActiveStorage::AnalyzeJob (Job ID: 15433d76-d866-4d41-b32b-5d2b5e04bf4c) from Async(default) in 48.51ms
active_storage_attachmentsテーブル
カラム名 | 値 |
---|---|
id | 1 |
name | avatar |
record_type | User |
record_id | 1 |
blob_id | 1 |
created_at | 2018-03-31 14:08:08.249993 |
active_storage_blobsテーブル
カラム名 | 値 |
---|---|
id | 1 |
key | dHerwrM2wUCeBdbGv8fBg41W |
filename | avatar.jpg |
content_type | image/jpeg |
metadata | {“identified”:true,“analyzed”:true} |
byte_size | 85092 |
checksum | qtC9awH9X+Lp5VABpTx0RQ== |
created_at | 2018-03-31 14:08:08.224345 |
表示
viewに
<%= image_tag(url_for(@user.avatar)) %>
とすれば画像が表示されます。
URL
今回ローカルストレージを使用して発行されたURLは
/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--02c53088de34ddb4f797a239219dbe05b2b5f8a5/avatar.jpg
でした。
アクセスしてみる
発行されたURLにアクセスするとcontent_typeなどのパラメータがついたURLにリダイレクトされる様です。
Started GET "/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--02c53088de34ddb4f797a239219dbe05b2b5f8a5/avatar.jpg" for 127.0.0.1 at 2018-04-01 00:12:46 +0900
Processing by ActiveStorage::BlobsController#show as JPEG
Parameters: {"signed_id"=>"eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--02c53088de34ddb4f797a239219dbe05b2b5f8a5", "filename"=>"avatar"}
ActiveStorage::Blob Load (0.2ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ vendor/bundle/ruby/2.4.0/gems/activerecord-5.2.0.rc2/lib/active_record/log_subscriber.rb:98
Disk Storage (1.4ms) Generated URL for file at key: dHerwrM2wUCeBdbGv8fBg41W (/rails/active_storage/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaDFrU0dWeWQzSk5NbmRWUTJWQ1pHSkhkamhtUW1jME1WY0dPZ1pGVkE9PSIsImV4cCI6IjIwMTgtMDMtMzFUMTU6MTc6NDYuNzA4WiIsInB1ciI6ImJsb2Jfa2V5In19--34377fd4cb46b8971454ae8431fe71d42ac979cc/avatar.jpg?content_type=image%2Fjpeg&disposition=inline%3B+filename%3D%22avatar.jpg%22%3B+filename%2A%3DUTF-8%27%27avatar.jpg)
Redirected to http://localhost:3000/rails/active_storage/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaDFrU0dWeWQzSk5NbmRWUTJWQ1pHSkhkamhtUW1jME1WY0dPZ1pGVkE9PSIsImV4cCI6IjIwMTgtMDMtMzFUMTU6MTc6NDYuNzA4WiIsInB1ciI6ImJsb2Jfa2V5In19--34377fd4cb46b8971454ae8431fe71d42ac979cc/avatar.jpg?content_type=image%2Fjpeg&disposition=inline%3B+filename%3D%22avatar.jpg%22%3B+filename%2A%3DUTF-8%27%27avatar.jpg
Completed 302 Found in 4ms (ActiveRecord: 0.2ms)
Started GET "/rails/active_storage/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaDFrU0dWeWQzSk5NbmRWUTJWQ1pHSkhkamhtUW1jME1WY0dPZ1pGVkE9PSIsImV4cCI6IjIwMTgtMDMtMzFUMTU6MTc6NDYuNzA4WiIsInB1ciI6ImJsb2Jfa2V5In19--34377fd4cb46b8971454ae8431fe71d42ac979cc/avatar.jpg?content_type=image%2Fjpeg&disposition=inline%3B+filename%3D%22avatar.jpg%22%3B+filename%2A%3DUTF-8%27%27avatar.jpg" for 127.0.0.1 at 2018-04-01 00:12:46 +0900
Processing by ActiveStorage::DiskController#show as JPEG
Parameters: {"content_type"=>"image/jpeg", "disposition"=>"inline; filename=\"avatar.jpg\"; filename*=UTF-8''avatar.jpg", "encoded_key"=>"eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaDFrU0dWeWQzSk5NbmRWUTJWQ1pHSkhkamhtUW1jME1WY0dPZ1pGVkE9PSIsImV4cCI6IjIwMTgtMDMtMzFUMTU6MTc6NDYuNzA4WiIsInB1ciI6ImJsb2Jfa2V5In19--34377fd4cb46b8971454ae8431fe71d42ac979cc", "filename"=>"avatar"}
Disk Storage (0.4ms) Downloaded file from key: dHerwrM2wUCeBdbGv8fBg41W
Rendering text template
Rendered text template (0.0ms)
Sent data (0.4ms)
Completed 200 OK in 2ms (Views: 0.3ms | ActiveRecord: 0.0ms)
今まで使っていたCarrierwaveでは保存したファイルのURLが直接発行されていたのですがActiveStorageでは挙動が少し違う様です。
本番でS3などを使った場合挙動がどうなるか今度調べてみようと思います。