31

I'm using Carrierwave to upload files, and I have it working.

My issue is attempting to change the name of the uploaded file.

In the generated uploader.rb there is a method I think I should be using

def filename
   "something.jpg" if original_filename
   basename = "what"+orginal_filename if original_filename, works
   basename = (0...8).map{65.+(rand(25)).chr}.join if original_filename  # will create a random name for each version, e.g. the orginal, the thumb, and the filename in the db, useless
 end

I can't seem to access items like 'extension' or 'content_type' in sanitized_file.rb, so this is a bit beyond my current skill level right now.

Any suggestions or exercises for doing this, i.e. generate filename for an uploaded file that works as well as the carrierwave default (do nothing, but does carry on to each version)? Seems like it should be simple enough but I've stumbled over this.

1
  • 3
    I don't quite understand your code with comments/code all mixed up. Can you edit it? Commented Aug 21, 2012 at 4:15

6 Answers 6

35

Well, another problem with your random filename generator is that it's possible to have collisions isn't it? You could possibly generate a filename that was already generated. One way to go about it would be to somehow generate a hash based on unique properties of the image, like file path. An example, from the carrierwave group:

def filename 
  if original_filename 
    @name ||= Digest::MD5.hexdigest(File.dirname(current_path))
    "#{@name}.#{file.extension}"
  end
end

This will create an MD5 hash based on the current path and then append the original file's extension to it.

Edit: The carrierwave wiki added an entry with a few methods on how to create random and unique filenames for all versioned files.

Sign up to request clarification or add additional context in comments.

4 Comments

This is definitely the best answer. Hashes are the right solution to this problem.
Assuming one has a rake task that goes in and deletes temp files, isn't the probability of a collision using just File.dirname(current_path) pretty high? I think a better solution might be to use created_at in combination with the path.
What about using a uuid? @name = SecureRandom.uuid
The if-statement should no longer be necessary as of CarrierWave 3.0 (see github.com/carrierwaveuploader/…)
5

To have a real unique filename (not almost unique) I recommend to use the uuid gem.

in Gemfile add:

gem 'uuid'

in file_uploader.rb:

def filename
  if original_filename
    if model && model.read_attribute(mounted_as).present?
      model.read_attribute(mounted_as)
    else
      @name ||= "#{mounted_as}-#{uuid}.#{file.extension}"
    end
  end
end

protected

def uuid
  UUID.state_file = false
  uuid = UUID.new
  uuid.generate
end

1 Comment

should i add this in the uploader?
1

From the Google Group:

def filename
  @name ||= "#{secure_token}.#{file.extension}" if original_filename
end

private

def secure_token
  ivar = "@#{mounted_as}_secure_token"
  token = model.instance_variable_get(ivar)
  token ||= model.instance_variable_set(ivar, ActiveSupport::SecureRandom.hex(4))  
end

Comments

1

To just make the record.id prefix the filename you can do the following:

class MyUploader < CarrierWave::Uploader::Base

  storage :file

  def store_dir
    model.class.to_s.underscore.pluralize
  end

  def filename
    model.id ? "#{model.id}-#{original_filename}" : original_filename
  end

  def url
    "/#{store_dir}/#{model.id}-#{model.file_before_type_cast}"
  end
end

Comments

1

The other solution looks good, but how I did it then was to have a hook that created a random string for a new name on instance creation, then:

 def filename
    "#{model.randomstring}.#{model.image.file.extension}"
 end

in the uploader.

That worked, putting the random name generation as part of the model, then having carrierwave use that.

I am curious which is faster, more effective, reasonable, sound, etc.

Comments

0

Here is the solution, how to change the name of the file, if store_dir already contains the file with the exact name:

  if File.exists?(Rails.root.join("documents/" + "#{file.filename}")) && !path.to_s.eql?(Rails.root.join("documents/" + original_filename).to_s)
    @name ||= File.basename(original_filename, '.*') + Digest::MD5.hexdigest(File.dirname(current_path)).from(25)
    "#{@name}.#{file.extension}"
  else
    "#{original_filename}"
  end

Note: Rails.root.join("documents/") is defined as my store_dir.

Hope it helps someone.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.