【Rails】Active Storageについて
はじめに
Rails学習時に調べたことや、遭遇したエラーなどについて書いております。
間違いなどありましたら、優しくご指摘いただけましたら幸いです。
対象読者
- Rails初学者の方
- Active Storageの概要を理解したい方
前提
当記事は、Rails7.0以降のバージョンを使用している前提の記事となります。
Active Storageとは?
Active StorageとはRailsが提供する公式のファイルアップロード機能のことです。
ローカルだけではなく、Amazon S3、Microsoft Azure Storage、Google Cloud Storageの3種類のクラウドストレージにも対応しています。
Active Storageは、主に以下の2つのモデルで構成されます。
-
ActiveStorage::Blob
:アップロードファイルのメタ情報を保持する -
ActiveStorage::Attachment
:モデルとActiveStorage::Blob
の中間テーブルの役割を担う
UserモデルもPostモデルも、ファイル保存にはAttachmentとBlobを使用するような構造になっています。
つまり、usersテーブルにもpostsテーブルにも新しくファイル保存用のカラムを追加する必要がないのです。
AttachmentとBlobには何が入っているのか?
それぞれのテーブルにはどんな情報を保持しているのか、schema.rbを見てみることで、より詳しく理解してみましょう。
(後述するActive Storageの導入方法を実行すると作成されます)
create_table "active_storage_blobs", charset: "utf8mb4", 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"
t.datetime "created_at", precision: nil, null: false
t.string "service_name", null: false
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
create_table "active_storage_attachments", charset: "utf8mb4", 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", precision: nil, 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
Blobの方にはファイル名やファイルの種類、サイズなど、アップロードされたファイルについてのメタ情報が格納されることがわかります。
また、Attachmentは、 まずrecord_type
と record_id
でファイルがアタッチされているモデルの情報がわかります。Postモデルであれば、record_type
に Post
、record_id
にPostのIDが入ります。さらに blob_id
でそのモデルに紐づくBlobの情報がわかるようになっているので、まさに中間テーブルのような役割をしていることがわかりますね。
Active Storageの導入方法
以下のコマンドを実行することでActive Storageを使用できるようになります。
bin/rails active_storage:install
bin/rails db:migrate
ファイルをレコードにアタッチする
すでに存在するモデルにActive Storage属性を追加したい場合
モデルにレコードとファイルのマッピングを設定します。
以下の例では、 has_one_attached
を記載することで、Postモデルに画像が1つマッピングされています。 :image
はファイルの名称に応じて名前を変更できます。
画像が複数ある場合は、 has_many_attached
を使用します。
class Post < ApplicationRecord
has_one_attached :image
end
Active Storage属性を含んだモデルを生成したい場合
以下のようにコマンドを実行することで実現できます。(Rails6.0以降)
bin/rails g model Post image:attachment
画像加工できるようにする
アップロードした画像をそのまま使うのではなく、リサイズしたいなど、画像加工を施すこともできます。
画像加工に必要なサードパーティソフトウェア・gem
画像加工を行うために必要となるのは、image_processing gemとlibvipsです。libvipsの代わりにImageMagickを使うこともできます。libvipsはImageMagickほど多くの画像形式をサポートしていませんが、メモリ消費量が少なく実行速度が速いことが特徴です。
image_processing gemのインストール
Gemfileに以下を追加して、 bundle install
を実行しましょう。
gem "image_processing", ">= 1.2"
実行すると、Gemfile.lockに以下が追加されています。
image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
MiniMagickは、RubyでImageMagickをより簡単に使えるようにしているものです。
もう一方のruby-vipsも同様に、libvipsの機能を使用するための接続部分となるものです。
libvipsのインストール
MacOSであれば、 brew install vips
を実行します。
Dockerを使用している場合は、Dockerfileに以下を記載します。
※ Debian系LinuxOS(Ubuntuなど)のコンテナイメージを作成する場合の例です。
RUN apt-get install -y libvips
Rails7.0以降は、画像加工・解析に使用するプロセッサのデフォルトが vips
になっているため、ImageMagickを使用する場合、libvipsと同様の方法でインストールした後、以下の設定が必要です。
config.active_storage.variant_processor = :mini_magick
画像のリサイズ
variant
メソッドを使用することで、サイズ違いの画像が作成できます。
<%= image_tag @post.image.variant(resize_to_limit: [100, 100]) %>
resize_to_limit
は、指定の寸法より大きい場合のみ、元の縦横比を維持したまま、指定の寸法に収まるように画像を縮小します。パラメータとして指定できるものは他にもありますので、詳しくは以下をご確認ください。
ちなみに、調べていた中で <%= image_tag @post.image.variant(resize: '100x100') %>
のような書き方をしているのを見かけることがありました。
Railsガイドも過去のバージョン(v6.1)で、このように resize
を使用した書き方をしています。
手元で動かしている環境はRails7.0.4ですが、 resize
を使って書き換えても動作しています。
使い分けがあるのか、 resize
は使うべきではないのかよくわからずかなり調べた結果、英語版のRailsガイドの修正点をまとめたようなファイルに、 resize
だとvips
を使った際にエラーになるために、resize_to_limit
のような書き方に修正すると書いてあるのを見つけました。
自分が書き換えて動かしてみた中ではエラーは生じなかったのですが、resize_to_limit
などの書き方が推奨されている様子が伺えますので、こちらを使っていけばいいのかなと思います。
画像アップロードライブラリの比較
Active Storageのように標準で使えるものだけではなく、画像アップロード機能にはCarrierWaveやShrineのようなGemもあります。
Active StorageはこれらのGemと比較してどのような点が異なるのでしょうか?
加工画像の生成タイミング
CarrierWave、Shrine、Active Storageは以下のようにすべて加工画像の生成タイミングが異なります。
画像アップロード機能 | 加工画像生成タイミング |
---|---|
CarrierWave | アップロード時 |
Shrine | アップロード後に非同期 |
Active Storage | 画像URLへのアクセス時 |
バリデーションヘルパーについて
画像をアップロードして欲しいフィールドにおいて、動画などの想定していないファイルのアップロードを防ぎたいというような状況があるかと思います。そのような場合、受け付けるファイルの種類を指定することで、アップロードできるファイルに制限をつけたいですよね。
CarrierWave、Shrine gemにはそのようなバリデーションの機能が備わっています。一方、Active Storageにはデフォルトでそのようなバリデーションヘルパーはなく、自力で実装するか、Active Storage Validations gemを利用する必要があります。
キャッシュ機能
フォーム入力時に、画像をアップロードしたあと、他のフィールドにおいてバリデーションエラーが起きてしまう場面を想定します。キャッシュ機能があれば、アップロードした画像はキャッシュから取得できるため、再度アップロードしなくてもよくなります。CarrierWave、Shrine gemにはこのようなキャッシュ機能がありますが、Active Storageには備わっていないため、バリデーションエラーでフォーム入力データが送信できなかった場合、再度ファイルをアップロードしなければなりません。
参考
- Railsガイド (https://railsguides.jp/active_storage_overview.html)
- パーフェクトRuby on Rails 増補改訂版 5-2
Discussion