`has_many_attached`に複数添付ファイルの合計バイトサイズを取得するメソッドを追加
has_many_attachedで管理する複数の添付ファイルについて、byte_sizeメソッドで合計バイトサイズを一度に取得できるようになりました。これにより、ストレージ使用量の検証やUIでの容量表示が簡潔に記述できます。
背景
Active Storageのhas_many_attachedは複数ファイルの添付を管理しますが、これまで添付ファイル群の合計サイズを取得するAPIが存在しませんでした。合計バイトサイズを得るには、blobs.pluck(:byte_size).sumやblobs.sum(&:byte_size)を呼び出す必要があり、利用側のコードに実装の詳細が露出していました。has_one_attachedに対応するAPIが存在する中で、has_many_attached側にのみこのヘルパーが欠けている状態が課題でした。
技術的な変更
ActiveStorage::Attached::Many クラスにbyte_sizeメソッドが追加されました。実装はblobs.pluck(:byte_size).sumの1行に集約されています。
変更後:
# Returns the combined size in bytes of all attached blobs.
#
# document.images.byte_size # => 2048
def byte_size
blobs.pluck(:byte_size).sum
end
blobs.pluck(:byte_size)はSQLのSELECT byte_size FROM active_storage_blobs WHERE ...に相当するクエリを発行し、Rubyオブジェクトとしてblobを全件ロードせずにバイトサイズの配列だけを取得します。その後.sumでRuby側で合算するため、メモリ効率に配慮した実装になっています。
テストでは、DBに永続化済みの添付と未永続の添付(=で代入した直後)の両方のケースが追加されており、どちらのシナリオでも正しく合計サイズを返すことが確認されています。
test "getting the combined byte size of persisted attachments" do
blobs = [ create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg") ]
@user.highlights.attach(blobs)
assert_equal blobs.sum(&:byte_size), @user.highlights.byte_size
end
test "getting the combined byte size of non persisted attachments" do
blobs = [ create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg") ]
@user.highlights = blobs
assert_equal blobs.sum(&:byte_size), @user.highlights.byte_size
end
設計判断
pluckを用いたクエリ集約が採用されました。blobs.map(&:byte_size).sumのようにblobオブジェクトを全件ロードする実装ではなく、pluckでバイトサイズの値だけを取得することで、blobのレコード数が増えても不要なカラムのロードを避けられます。
また、メソッドの追加先をActiveStorage::Attached::Manyに限定しており、has_one_attachedに対応するActiveStorage::Attached::Oneには変更がありません。has_one_attachedの場合はblobが1つでありblob.byte_sizeで直接アクセスできるため、Many側のみに集約ヘルパーを設ける設計は自然な判断といえます。
まとめ
本PRは1メソッドの追加という小さな変更ながら、has_many_attachedのAPIに欠けていたストレージ計測の起点を提供します。blobs.pluckを用いた実装により、添付ファイル数が増加してもメモリ効率を保ちながら合計サイズを取得でき、ファイルサイズバリデーションや容量表示といったユースケースで利用側のコードをシンプルに保てます。