Active Storageについて

Active Storageについて

Active Storageは、モデルにカラムを持たせることなく画像のアップロードを実現できる機能
Active Storageでは、モデルオブジェクトに画像ファイルをアタッチする(アタッチ: 所属する)
Active Storageでは、以下の2つのテーブルが生成され、モデルオブジェクトにマッピングしている

・active_storage_attachments: モデルオブジェクトとactive_storage_blobsとの中間テーブル
・active_storage_blobs: 実際に画像が保存されてる場所を管理するようなテーブル(keyやfilenameなどが格納されている)

Active Storageの主な使い方は、以下になる

$ rails active_storage:install


$ rails db:migrate


アプリケーションが使うサービスごとに以下のように定義する

[config/storage.yml]

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

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


利用するサービスをActive Storageに認識させるには、以下のように記述する
以下は、ローカルDiskサービスをdevelopment環境で使う場合

[config/environments/development.rb]

config.active_storage.service = :local

# ここで指定している画像の保存先のlocalは、[config/storage.yml]で定義したもの


[has_one_attached]を定義して、レコードとファイルの間に1対1のマッピングを設定する(1対多の時は、[has_many_attached]を定義)
モデルにカラムを追加しているわけではなく、ストロングパラメーターで受け取る時は、モデルに定義した他の属性と一緒に受け取れる

[app/models ファイル]

has_one_attached :カラム名


モデルオブジェクトにアタッチしているファイルの有無を確認する時は、以下のようにして確認できる

モデルオブジェクト.カラム名.attached?

# 以下が使用例
user.image.attached?

ファイルを表示する時は、以下のようにして表示できる

<%= image_tag user.image %>

画像を削除する時は、以下のようにして削除できる

user.image.purge


今のままの状態だと表示される画像の大きさが元の画像の大きさのままなので、[ImageMagick]という画像変換ツールをインストールする

$ brew install imagemagick


[ImageMagick]をrailsで使えるようにするために、[mini_magick]Gemをインストールする

[Gemfile]

gem 'mini_magick'


画像の幅を指定したい時は、以下のようにして幅を指定して表示できる

<%= image_tag user.image.variant(resize:'300x300') %>

※ポイント
Active Storageの画像の削除は、中間テーブルである[active_storage_blobs]テーブルからデータを削除するだけでも連動して[active_storage_blobs]テーブルからも対象のデータを削除してくれる(以下が実験した内容)

[1] pry(main)> ActiveStorage::Attachment.where(name: "favicon")
  ActiveStorage::Attachment Load (0.2ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."name" = ?  [["name", "favicon"]]
=> [#<ActiveStorage::Attachment:0x00007f9f0a930ac0
  id: 66,
  name: "favicon",
  record_type: "Site",
  record_id: 1,
  blob_id: 66,
  created_at: Mon, 06 Sep 2021 18:30:10 JST +09:00>]
[2] pry(main)> image = ActiveStorage::Attachment.find(66)
  ActiveStorage::Attachment Load (0.3ms)  SELECT  "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."id" = ? LIMIT ?  [["id", 66], ["LIMIT", 1]]
=> #<ActiveStorage::Attachment:0x00007f9eed2341f8
 id: 66,
 name: "favicon",
 record_type: "Site",
 record_id: 1,
 blob_id: 66,
 created_at: Mon, 06 Sep 2021 18:30:10 JST +09:00>
[3] pry(main)> image
=> #<ActiveStorage::Attachment:0x00007f9eed2341f8
 id: 66,
 name: "favicon",
 record_type: "Site",
 record_id: 1,
 blob_id: 66,
 created_at: Mon, 06 Sep 2021 18:30:10 JST +09:00>
[4] pry(main)> image.purge
   (0.1ms)  begin transaction
  ActiveStorage::Attachment Destroy (0.8ms)  DELETE FROM "active_storage_attachments" WHERE "active_storage_attachments"."id" = ?  [["id", 66]]
  Site Load (0.1ms)  SELECT  "sites".* FROM "sites" WHERE "sites"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Site Update (0.2ms)  UPDATE "sites" SET "updated_at" = ? WHERE "sites"."id" = ?  [["updated_at", "2021-09-06 18:33:59.094072"], ["id", 1]]
   (0.9ms)  commit transaction
   (0.2ms)  select sql from (select * from sqlite_master where type='table' union select * from sqlite_temp_master where type='table') where tbl_name = 'active_storage_blobs'
  ActiveStorage::Blob Load (0.2ms)  SELECT  "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = ? LIMIT ?  [["id", 66], ["LIMIT", 1]]
   (0.1ms)  begin transaction
  ActiveStorage::Attachment Exists (0.2ms)  SELECT  1 AS one FROM "active_storage_attachments" WHERE "active_storage_attachments"."blob_id" = ? LIMIT ?  [["blob_id", 66], ["LIMIT", 1]]
  ActiveStorage::Blob Destroy (0.6ms)  DELETE FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = ?  [["id", 66]]
   (1.6ms)  commit transaction
  ActiveStorage::Attachment Load (0.2ms)  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", 66], ["record_type", "ActiveStorage::Blob"], ["name", "preview_image"], ["LIMIT", 1]]
  Disk Storage (0.4ms) Deleted file from key: KF9bAwWS6tNWnmF8SEgUQ4Rn
  Disk Storage (0.5ms) Deleted files by key prefix: variants/KF9bAwWS6tNWnmF8SEgUQ4Rn/
=> ["/Users/higmonta/workspace/runteq/application_tasks/2563_higmonta_runteq_curriculum_advanced/tmp/storage/va/ri/variants/KF9bAwWS6tNWnmF8SEgUQ4Rn/c5d249056ad8a9a7cea59197db22bc5591a40e7cddb3ed3912d5f4fb529e84be",
 "/Users/higmonta/workspace/runteq/application_tasks/2563_higmonta_runteq_curriculum_advanced/tmp/storage/va/ri/variants/KF9bAwWS6tNWnmF8SEgUQ4Rn/507e6b2952e87503b6ff212d6c1ba25a9f0bec18f0e49c952d43737c702fe518"]

参考記事

【Rails】 Active | Pikawaka - ピカ1わかりやすいプログラミング用語サイト

【Rails 5.2】 Active Storageの使い方 - Qiita

ActiveStorage - Webとデザインのあれこれ

コマンドラインで画像処理が行える便利ツール「ImageMagick」:知っトクWindowsツール(1/2 ページ) - @IT

Active StorageのVariantの指定方法いろいろ - Qiita