Description
I use the following to allow attaching of remote files:
module ActiveStorage
module RemoteURLHelper
# If the record is persisted and unchanged, the attachments are saved to
# the database immediately. Otherwise, they'll be saved to the DB when the
# record is next saved.
def attach_from_url(url, max_size: nil)
tempfile = Down.download(url, max_size: max_size)
attach(io: tempfile, filename: tempfile.original_filename, content_type: tempfile.content_type)
end
end
end
Rails.application.config.after_initialize do
ActiveStorage::Attached::One.include(ActiveStorage::RemoteURLHelper)
ActiveStorage::Attached::Many.include(ActiveStorage::RemoteURLHelper)
end
This allows me to do things like:
gallery.photos.attach_from_url "https://example.com/photo1.png"
gallery.photos.attach_from_url "https://example.com/photo2.png"
gallery.photos.attach_from_url "https://example.com/photo3.png"
gallery.save!
This works as expected, but active_storage_validations 1.1.4
does not seem compatible with this approach.
The reason is that the above won't upload the photos to the cloud storage provider yet. So when we call gallery.save!
and it runs the validations, we get an ActiveStorage::FileNotFoundError (ActiveStorage::FileNotFoundError)
error in activestorage-7.1.3/lib/active_storage/service/s3_service.rb:159:in stream: ActiveStorage::FileNotFoundError
After a little digging, I found out the reason for this:
active_storage_validations/lib/active_storage_validations/metadata.rb
Lines 74 to 84 in dd835e6
This code assumes the blob
is already saved and uploaded. However, with the above code example, the blob will not be persisted yet. So blob.id
will be nil
and blob.download
raises the exception.
Here's a relevant code comment from activestorage/lib/active_storage/attached/many.rb and activestorage/lib/active_storage/attached/one.rb's attach(…)
methods:
# If the record is persisted and unchanged, the attachments are saved to
# the database immediately. Otherwise, they'll be saved to the DB when the
# record is next saved.
def attach(…)
So it seems to be that approach from my ActiveStorage::RemoteURLHelper
mixin is indeed a valid use of the ActiveStorage API's, but active_storage_validations
is currently incompatible with the scenario where the blobs aren't persisted yet.
Unfortunately, I don't see an easy solution as ActiveStorageValidations::Metadata
is only passed the file (a blob in this case), and an unsaved blob, as far as I know, does have no knowledge of its io
as that's typically passed in externally right before it gets uploaded.
The proper approach might be to not pass the blob, but the ActiveStorage::Attached::One
/ ActiveStorage::Attached::Many
instead.
Activity