ActiveStorageダイレクトアップロードでファイルサイズの制限を設けるには
ActiveStorageのダイレクトアップロード機能を利用すると、ブラウザから直接クラウドストレージにアップロードできるため、Railsアプリで大容量のデータを受け取ることなく、大容量ファイルのアップロードも可能です。
しかしながら、無制限に受け入れていては、想定外の大容量ファイルがアップロードされてしまう可能性があります。たとえば、数MBの画像の想定のところ1TBの動画がアップロードされると困ってしまいますね。
ActiveStorageでファイル容量のバリデーションを行うには
ActiveStorageのバリデーションを実装するgemはいくつかあります。
次のように書くと、1MB以上のファイルを禁止することができます。
class Post < ApplicationRecord
has_one_attached :image
validates :image, attached: true, size: { less_than: 1.megabytes , message: 'is too large' }
end
しかし、これだけでは、Postの保存は防げますが、クラウドストレージへのダイレクトアップロードは防げません。
ダイレクトアップロード可能なファイル容量のバリデーションを行うには
ActiveStorageのダイレクトアップロードは以下の手順で行われます。
- ダイレクトアップロードのための署名付きURLとHTTPヘッダーを生成
- アップロードするファイルの情報をPOST(
/rails/active_storage/direct_uploads
)- ファイル容量やファイル種別などをActiveStorageに伝える
ActiveStorage::Blob.create_before_direct_upload!
- ダイレクトアップロードに必要な署名付きURLなどを取得
- アップロードするファイルの情報をPOST(
- クラウドストレージへのアップロード
- 1で生成した署名付きURLとHTTPヘッダーを使用
- 署名により、事前に申告したファイル容量・ファイル種別以外はアップロードできない
-
Post
とActiveStorage::Blob
の紐づけ
Postモデルのバリデーションは上記手順の3で行うため、2のアップロードを防げません。
アップロード自体を禁止するためには、1でエラーを返す必要があります。
クラウドストレージへのダイレクトアップロードを禁止する方法としては、例えば次のような方法があります。
- S3 POST Policy を使う
- S3バックエンドの場合、S3ではPOST Policyという仕組みでアップロードについてのルールを設定できます
- パラメータのチェックを行う、カスタム
DirectUploadsController
を作成して、fileフィールドのdirect-upload-url=
で指定する -
ActiveStorage::Blob
にバリデーションを追加する
S3 POST Policy を使う
クラウドストレージへのアップロード時のHTTPヘッダーとしてPOST PolicyとPolicyを含むSignatureを送る必要がありますが、ActiveStorageではこれらに対応することはできません。
従ってこの方法は使えません。
DirectUploadsController
を作成して、fileフィールドのdirect-upload-url=
で指定する
パラメータのチェックを行う、カスタム この方法では f.file_field :file, data: { 'direct-upload-url': my_direct_uploads_path }
のように指定する必要があり、使い勝手が悪いです。また、標準の rails_direct_uploads_path
が残っていると、そちらからアップロードされる可能性も残るので確実ではありません。従って避けるべきだと考えます。
ActiveStorage::Blob にバリデーションを追加する
オープンクラスであることを利用して、ActiveStorage::Blob
にバリデーションを追加する方法です。
これにより、要件を満たさない場合にダイレクトアップロードに必要な署名付きURLやヘッダーを生成できず、ダイレクトアップロードを事前に防止することができました。
ただし、このバリデーションはattachされた特定のモデルに対してではなく、Railsアプリ全体のダイレクトアップロードに適用される点に注意してください。
# config/initializers/set_byte_size_limit_on_blobs.rb
ActiveSupport.on_load(:active_storage_blob) do
validates :byte_size, numericality: { less_than: 10.megabytes }
end
ActiveSupport.on_load(:active_storage_blob)
ActiveSupport.on_load(:active_storage_blob, &block)
の block
は、ActiveStorage::Blob
が読み込まれた際に、ActiveStorage::Blob
のコンテキストで実行されます。
まとめ
ActiveRecord::Blobをattachするモデルのバリデーション(モデルごとのバリデーション)と、ActiveRecord::Blobのバリデーション(アプリケーション全体のバリデーション)を組み合わせることで、
- 巨大なファイルのダイレクトアップロードを防止する
- 特定のモデルでさらに細かな制限を設ける
ことができます。
Discussion