Skip to content

How to: Create random and unique filenames for all versioned files

clyfe edited this page Mar 8, 2012 · 23 revisions

Both of the methods below are available from Ruby 1.8.7 onwards.

NOTE! SecureRandom.uuid is used just as an example. You should definitively use a truly unique id generator (not a random one) or have a uniqueness constraint in DB and retry a unique find.

Unique filenames

The following will generate UUID filenames in the following format:

1df094eb-c2b1-4689-90dd-790046d38025.jpg

someversion_1df094eb-c2b1-4689-90dd-790046d38025.jpg

class PhotoUploader < CarrierWave::Uploader::Base
  def filename
     @name ||= "#{secure_token}.#{file.extension}" if original_filename.present?
  end

  protected
  def secure_token
    var = :"@#{mounted_as}_secure_token"
    model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
  end
end

Note

if you do recreate_versions! this method will encode the filename of the previously encoded name, which will result in a new name. If you want to keep the previously encoded name, there is a workaround:

class AvatarUploader < CarrierWave::Uploader::Base
  def filename
    if original_filename
      if model && model.read_attribute(:avatar).present?
        model.read_attribute(:avatar)
      else
        # new filename
      end
    end
  end
end

Random filenames

The following will generate hexadecimal filenames in the following format:

43527f5b0d.jpg

someversion_43527f5b0d.jpg

The length of the random filename is determined by the paramater to secure_token() within the filename method. The shorter the filename, the more chance of duplicates occurring. Unless you have a specific need for shorter filenames, it is recommended to use unique filenames instead (see above).

class PhotoUploader < CarrierWave::Uploader::Base
  def filename
     "#{secure_token(10)}.#{file.extension}" if original_filename.present?
  end

  protected
  def secure_token(length=16)
    var = :"@#{mounted_as}_secure_token"
    model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.hex(length/2))
  end
end

Note

If you're using the methods described above, it might be a good idea to store tokens in a database column:

  def secure_token(length = 16)
    model.image_secure_token ||= SecureRandom.hex(length / 2)
  end

Instance variables won't be persisted which means that if you're somehow manipulating existing images (e.g. cropping), they will be created under different filenames and not assigned to the model properly.

(Related: How to: Use a timestamp in file names)

Clone this wiki locally