ActiveStorageでattachできるものについて調べてみた

福田です

ActiveStorage便利ですよね

railsのmodelにファイルを簡単に紐づけることができるんですが、form postされたファイルだけじゃなくてdirect uploadで発行された謎トークンとかもattachできて混乱しちゃったので、結局何がattachできるのかコード読んで調べてみました

↓はhas_oneなattachのコードです

# Associates a given attachment with the current record, saving it to the database.
#
#   person.avatar.attach(params[:avatar]) # ActionDispatch::Http::UploadedFile object
#   person.avatar.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload
#   person.avatar.attach(io: File.open("/path/to/face.jpg"), filename: "face.jpg", content_type: "image/jpg")
#   person.avatar.attach(avatar_blob) # ActiveStorage::Blob object
def attach(attachable)
    blob_was = blob if attached?
    blob = create_blob_from(attachable)

    unless blob == blob_was
        transaction do
            detach
            write_attachment build_attachment(blob: blob)
        end
        blob_was.purge_later if blob_was && dependent == :purge_later
    end
end

どうやらattachは四種類の引数を受け取るようです

  • ActionDispatch::Http::UploadedFile
    • form postされたfile
  • signed_blob_id
    • ファイルそのものではなくてdirect uploadでアップロードされたファイルを表すID?
  • io
  • ActiveStorage::Blob
    • active_storage_blobsかな?
    • すでにattachされてるファイルを別のmodelに紐づけたりできそう

IOクラスのインスタンスを直接attachできるのは初めて知ったので試してみましょう

User model作って

class User < ApplicationRecord
    has_one_attached :avatar
end

適当に置いといたpngをattachしてみる

irb(main):012:0> f = File.open(Rails.root.join('storage', 'sample_avatar.png').to_s)
=> #<File:/app/storage/sample_avatar.png>
irb(main):013:0> User.first.avatar.attach(io: f, filename: 'sample_avatar.png', content_type: 'image/png')

urlを取得してみる

irb(main):023:0* include Rails.application.routes.url_helpers
=> Object
irb(main):024:0> default_url_options[:host] = "localhost:3000"
=> "localhost:3000"
irb(main):025:0> url_for User.first.avatar
  User Load (0.3ms)  SELECT  `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
  ActiveStorage::Attachment Load (1.9ms)  SELECT  `active_storage_attachments`.* FROM `active_storage_attachments` WHERE `active_storage_attachments`.`record_id` = 1 AND `active_storage_attachments`.`record_type` = 'User' AND `active_storage_attachments`.`name` = 'avatar' LIMIT 1
  ActiveStorage::Blob Load (0.4ms)  SELECT  `active_storage_blobs`.* FROM `active_storage_blobs` WHERE `active_storage_blobs`.`id` = 1 LIMIT 1
=> "http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--c0c210c1622d0f9c9d208352011ee63a9aa8d5f1/sample_avatar.png"

ちゃんとattachできてる

IOのインスタンスってことはopen-uriとか使ってweb上の画像とかも直接attachできるので、SNS連携で登録された時のデフォルトアバターとか設定するのに使えそうですね