How do I duplicate a file stored in ActiveStorage in Rails 5.2
Found the answer by looking through Rails's tests, specifically in the blob model test
So for this case
class Package < ApplicationRecord
has_one_attached :poster_image
end
You can duplicate the attachment as such
original = Package.first
copy = original.dup
copy.poster_image.attach \
:io => StringIO.new(original.poster_image.download),
:filename => original.poster_image.filename,
:content_type => original.poster_image.content_type
The same approach works with has_many_attachments
class Post < ApplicationRecord
has_many_attached :images
end
original = Post.first
copy = original.dup
original.images.each do |image|
copy.images.attach \
:io => StringIO.new(image.download),
:filename => image.filename,
:content_type => image.content_type
end
Update your model:
class Package < ApplicationRecord
has_one_attached :poster_image
end
Attach the source package’s poster image blob to the destination package:
source_package.dup.tap do |destination_package|
destination_package.poster_image.attach(source_package.poster_image.blob)
end
It worked for me:
copy.poster_image.attach(original.poster_image.blob)
If you want a full copy of the file so that both the original record and the cloned record have their own copy of the attached file, do this:
In Rails 5.2, grab this code and put it in config/initializers/active_storage.rb
, then use this code to do a copy:
ActiveStorage::Downloader.new(original.poster_image).download_blob_to_tempfile do |tempfile|
copy.poster_image.attach({
io: tempfile,
filename: original.poster_image.blob.filename,
content_type: original.poster_image.blob.content_type
})
end
After Rails 5.2 (whenever a release includes this commit), then you can just do this:
original.poster_image.blob.open do |tempfile|
copy.poster_image.attach({
io: tempfile,
filename: original.poster_image.blob.filename,
content_type: original.poster_image.blob.content_type
})
end
Thanks, George, for your original answer and for your Rails contributions. :)