Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I duplicate a file stored in ActiveStorage in Rails 5.2

I have a model that is using ActiveStorage:

class Package < ApplicationRecord
  has_one_attached :poster_image
end

How do I create a copy of a Package object that contains a duplicate of the initial poster_image file. Something along the lines of:

original = Package.first
copy = original.dup
copy.poster_image.attach = original.poster_image.copy_of_file
like image 396
Robban Avatar asked Apr 03 '18 13:04

Robban


People also ask

How ActiveStorage works?

Using Active Storage, an application can transform image uploads or generate image representations of non-image uploads like PDFs and videos, and extract metadata from arbitrary files.

What is Active storage in Rails?

Active storage is an inbuilt gem in Rails that developers widely use to handle file uploads. Combined with the encrypted credentials feature in the latest releases of Rails, active storage is a safe and easy method to upload, serve, and analyze files onto cloud-based storage services as well as local storage.


4 Answers

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
like image 163
George Claghorn Avatar answered Oct 23 '22 16:10

George Claghorn


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. :)

like image 31
Benjamin Curtis Avatar answered Oct 23 '22 17:10

Benjamin Curtis


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
like image 44
jethro Avatar answered Oct 23 '22 17:10

jethro


It worked for me:

copy.poster_image.attach(original.poster_image.blob)
like image 39
Evan Avatar answered Oct 23 '22 18:10

Evan