Pablo Fernandez
Pablo Fernandez

Reputation: 287530

What's the proper way to copy a carrierwave file from one record to another?

I need to copy a file from one carrier wave object to another. They are different tables and different types of uploaders.

I started with:

user.avatar = image.content

(where user and image are model instances, avatar and content are the carrierwave mounted uploaders) which worked sometimes. It seems to work all the time locally, with a file storage, but intermittent when using fog and s3.

In a mailing list post I found this code:

user.avatar = image.content.file

that again worked sometimes.

My working solution so far is:

require "open-uri"

begin
  user.avatar = open(image.url)
rescue Errno::ENOENT => e
  begin
    user.avatar = open(image.path)
  rescue Errno::ENOENT => e
    # Ok, whatever.
  end
end

which is not only ugly, but fails to pass the extension validation because the opening of a remote file doesn't maintain the extension (jpg, png, etc.).

Upvotes: 21

Views: 12099

Answers (6)

ianYa
ianYa

Reputation: 19

You can do this by copying files.

store_path is a carrierwave method from Uploader class. It returns uploaded file's folder relative path.

this clone file method should be called after model record is saved. If record not saved, store_path may return wrong path if you specify store_dir with model id in uploader.

def clone_carrierwave_file(column_name)
  origin_files = Dir[File.join(Rails.root, 'public', original_record.send(column_name).store_path, '*')]
  return if origin_files.blank?
  new_file_folder = File.join(Rails.root, 'public', send(column_name).store_path)
  FileUtils.mkdir new_file_folder if !Dir.exist? new_file_folder
  FileUtils.cp(origin_files, new_file_folder)
end

Hope it works.

Upvotes: 1

PressingOnAlways
PressingOnAlways

Reputation: 12356

I needed to copy a reference from one model to another model and I was successfully able to do so by doing the following:

my_new_model.update_column('attachment', my_other_model.attributes["attachment"]);

In this scenario, I did not care to actually make a copy of the file, nor did I care that 2 records were now linked to the same file (my system never deletes or modifies files after uploaded).

This may be useful to anyone who wants to just copy the reference to a file from one model to another model using the same uploader.

Upvotes: 1

Brett Donald
Brett Donald

Reputation: 14147

I just wanted to copy an avatar reference from one object to another, and what worked for me was:

objectB.avatar.retrieve_from_store!(objectA.avatar.identifier)
objectB.save

Upvotes: 0

Dia Kharrat
Dia Kharrat

Reputation: 6006

Perhaps one way you can do it is to set a remote image URL as per the Carrierwave gem documentation?

user.remote_avatar_url = image.url

Upvotes: 15

Daniel Upton
Daniel Upton

Reputation: 5741

Here's a (albeit hacky) solution to that doesn't require an HTTP request to fetch the image:

module UploadCopier
  def self.copy(old, new)
    new.instance_variable_set('@_mounters', nil)

    old.class.uploaders.each do |column, uploader|
      new.send("#{column}=", old.send(column))
    end
  end
end

old_user = User.last
new_user = User.new
UploadCopier.copy(old_user, new_user)
new_user.save

Upvotes: 1

equivalent8
equivalent8

Reputation: 14237

From solutions discussed here I created simple CopyCarrierwaveFile gem to do this

usage is something like this:

original_resource = User.last
new_resource      = User.new

CopyCarrierwaveFile::CopyFileService.new(original_resource, new_resource, :avatar).set_file    

new_resource.save
nev_resource.avatar.url # https://...image.jpg

Upvotes: 9

Related Questions