Renombrando archivos subidos con Carrierwave

Estoy usando Carrierwave para subir archivos, y lo tengo funcionando.

Mi problema es intentar cambiar el nombre del archivo cargado.

En el uploader.rb generado hay un método que creo que debería usar

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, eg the orginal, the thumb, and the filename in the db, useless end 

Parece que no puedo acceder a elementos como ‘extensión’ o ‘tipo_contenido’ en sanitized_file.rb, así que esto está un poco más allá de mi nivel de habilidad actual en este momento.

¿Alguna sugerencia o ejercicio para hacer esto, es decir, generar un nombre de archivo para un archivo cargado que funcione tan bien como el valor predeterminado de carrierwave (no haga nada, pero continúe con cada versión)? Parece que debería ser lo suficientemente simple pero me he topado con esto.

Bueno, otro problema con su generador de nombre de archivo aleatorio es que es posible tener colisiones, ¿no es así? Posiblemente podrías generar un nombre de archivo que ya fue generado. Una forma de hacerlo sería generar un hash basado en propiedades únicas de la imagen, como la ruta del archivo. Un ejemplo, del grupo carrierwave:

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

Esto creará un hash MD5 basado en la ruta actual y luego le agregará la extensión del archivo original.

Edición: el wiki de carrierwave agregó una entrada con algunos métodos sobre cómo crear nombres de archivos aleatorios y únicos para todos los archivos versionados.

Para tener un nombre de archivo realmente único (no casi único) recomiendo usar la gem uuid.

en Gemfile agregar:

 gem 'uuid' 

en 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 

Del grupo de Google :

 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 

Para hacer que el prefijo record.id sea el nombre del archivo, puede hacer lo siguiente:

 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 

La otra solución se ve bien, pero cómo lo hice entonces fue para tener un gancho que creó una cadena aleatoria para un nuevo nombre en la creación de la instancia, entonces:

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

en el cargador

Eso funcionó, colocando la generación de nombres aleatorios como parte del modelo y luego haciendo que carrierwave usara eso.

Tengo curiosidad por saber qué es más rápido, más efectivo, razonable, sano, etc.

Aquí está la solución, cómo cambiar el nombre del archivo, si store_dir ya contiene el archivo con el nombre exacto :

  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 

Nota : Rails.root.join("documents/") se define como mi store_dir .

Espero que ayude a alguien.