5

I cant figure out how to update/rename a file uploaded/managed with Carrierwave-mongoid in rails 3.2.6. I want to rename the file in the db as well as on the filesystem.

Something like this maybe...

def rename( id , new_name )
  f = UploadedFile.find(id)

  if f.update_attributes({ f.file.original_filename: new_name })  # this is WRONG, what is right???
    new_path = File.join( File.dirname( f.file.current_path ) , new_name ))
    FileUtils.mv( f.file.current_path , new_path )
  end

  return f
end

Let me add this is after it has been uploaded already.

user892583
  • 267
  • 3
  • 13

6 Answers6

5

I was able to get the following working, although I'm sure there is a more elegant way. I'd appreciate any comments on the following

*add this to app/uploaders/file_uploader.rb

def rename(new_name)
  sf = model.file.file
  new_path = File.join( File.dirname( sf.file ) , "#{new_name}#{File.extname( sf.file )}")
  new_sf = CarrierWave::SanitizedFile.new sf.move_to(new_path)
  model.file.cache!(new_sf)
  model.save!
  return model
end

Thanks!

user892583
  • 267
  • 3
  • 13
  • duh! This is actually storing the file twice, once from cache! and once from move_to. – user892583 Oct 19 '12 at 22:11
  • I needed to convert the format of previously uploaded files to .jpg, and then get carrierwave to see them as the uploaded file. Your code here put be on the right path with SanitizedFile.new which I called on the converted/renamed file to get it associated my model. I discovered that I needed to call recreate_versions! after saving the model to get the versions generated properly. – Chris G. Jul 30 '13 at 21:09
2

The most efficient way to do this is to just move the existing S3 object (assuming your storage layer is S3):

def rename(new_name)
  bucket_name = "yourapp-#{Rails.env}"
  resource = Aws::S3::Resource.new
  bucket = resource.bucket(bucket_name)
  object = bucket.object(path)
  new_filename = "#{new_name}#{File.extname(path)}"
  new_path = File.join(File.dirname(path), new_filename)
  object.move_to(bucket: bucket_name, key: new_path)

  model.update_column(mounted_as, new_filename)
  model.reload

  # Now call `recreate_versions!(*versions.keys)`
  # if you want versions updated. Explicitly passing
  # versions will prevent the base version getting
  # reuploaded.

  model
end

This is using the aws-sdk-s3 gem.

Maros
  • 1,825
  • 4
  • 25
  • 56
1

I store image files -- and derivative versions -- in an S3-compatible solution. I use Carrierwave (1.2.2) with the "fog-aws" gem (3.0.0) on Rails 5.1. The following public method works for me when added to the "uploader" file (eg, app/uploaders/example_uploader.rb):

class ExampleUploader < CarrierWave::Uploader::Base
  <snip>

  # Renames original file and versions to match given filename
  #
  # Options:
  # * +:keep_original+ - Do not remove original file and versions (ie, copy only)
  def rename(new_filename, options = {})
    return if !file || new_filename == file.filename
    target = File.join(store_path, new_filename)
    file.copy_to(target)
    versions.keys.each do |k|
      target = File.join(store_path, "#{k}_#{new_filename}")
      version = send(k).file
      version.copy_to(target)
    end
    remove! unless options[:keep_original]
    model.update_column(mounted_as, new_filename) && model.reload
  end

  <snip>
end
milkfarm
  • 308
  • 2
  • 7
0

I used this rake task for reprocessing uploaded images after modifying version settings (filename and image size) in my uploader file:

# Usage: rake carrierwave:reprocess class=Model
namespace :carrierwave do
  task :reprocess => :environment do

    CLASS = ENV['class'].capitalize
    MODEL = Kernel.const_get(CLASS)
    records = MODEL.all

    records.each do |record|
      record.photo.recreate_versions! if record.photo?
    end

  end
end

Notes:

  • Replace "photo" with whatever you named your uploader.
  • Rake tasks go in the lib/tasks folder.
  • This is using Active Record, not sure if Mongoid needs something different.
Brian
  • 1,028
  • 12
  • 20
0

Based on @user892583, I worked on it and came up with a simpler solution:

def rename!(new_name)
  new_path = File.join(File.dirname(file.file), new_name)
  file.move_to(new_path)
end
0

I did this with this way:

  def filename
    if !cached? && file.present?
      new_filename = 'foobar'
      new_path = File.join(File.dirname(file.path), new_filename)
      file.move_to(new_path)
      recreate_versions!
      new_filename
    else
      super
    end
  end

I think this is only right way to rename file.

Artem P
  • 5,198
  • 5
  • 40
  • 44