Paperclip cambiar el nombre de los archivos después de que se guardan

¿Cómo cambio el nombre de un archivo después de que se haya cargado y guardado? Mi problema es que necesito analizar información sobre los archivos automáticamente para poder encontrar el nombre del archivo, el archivo debe guardarse como en mi aplicación, pero no puedo acceder a la información requerida para generar el nombre del archivo hasta el registro de El modelo ha sido guardado.

Si, por ejemplo, su modelo tiene imagen de atributo:

has_attached_file :image, :styles => { ...... } 

Por defecto, los archivos de clip de papel se almacenan en / system /: attachment /: id /: style /: filename.

Por lo tanto, puede lograrlo cambiando el nombre de cada estilo y luego cambiando la columna image_file_name en la base de datos.

 (record.image.styles.keys+[:original]).each do |style| path = record.image.path(style) FileUtils.move(path, File.join(File.dirname(path), new_file_name)) end record.image_file_name = new_file_name record.save 

¿Has comprobado las interpolaciones de clip ?

Si es algo que puede averiguar en el controlador (antes de que se guarde), puede usar una combinación del controlador, el modelo y la interpolación para resolver su problema.

Tengo este ejemplo donde quiero nombrar un archivo basado en su hash MD5.

En mi controlador tengo:

 params[:upload][:md5] = Digest::MD5.file(file.path).hexdigest 

Entonces tengo un config/initializers/paperclip.rb con:

 Paperclip.interpolates :md5 do|attachment,style| attachment.instance.md5 end 

Finalmente, en mi modelo tengo:

 validates_attachment_presence :upload has_attached_file :upload, :path => ':rails_root/public/files/:md5.:extension', :url => '/files/:md5.:extension' 

Para agregar a la respuesta de @ Voyta, si está utilizando S3 con un clip:

 (record.image.styles.keys+[:original]).each do |style| AWS::S3::S3Object.move_to record.image.path(style), new_file_path, record.image.bucket_name end record.update_attribute(:image_file_name, new_file_name) 

Las imágenes de mi avatar se nombran con la barra de usuario, si cambian sus nombres, también tengo que cambiar el nombre de las imágenes.

Así es como cambio el nombre de mis imágenes de avatar utilizando S3 y un clip.

 class User < ActiveRecord::Base after_update :rename_attached_files_if_needed has_attached_file :avatar_image, :storage => :s3, :s3_credentials => "#{Rails.root}/config/s3.yml", :path => "/users/:id/:style/:slug.:extension", :default_url => "/images/users_default.gif", :styles => { mini: "50x50>", normal: "100x100>", bigger: "150x150>" } def slug return name.parameterize if name "unknown" end def rename_attached_files_if_needed return if !name_changed? || avatar_image_updated_at_changed? (avatar_image.styles.keys+[:original]).each do |style| extension = Paperclip::Interpolations.extension(self.avatar_image, style) old_path = "users/#{id}/#{style}/#{name_was.parameterize}#{extension}" new_path = "users/#{id}/#{style}/#{name.parameterize}#{extension}" avatar_image.s3_bucket.objects[old_path].move_to new_path, acl: :public_read end end end 

Y para agregar otra respuesta, aquí está el método completo que estoy usando para cambiar el nombre de S3:

  def rename(key, new_name) file_name = (key.to_s+"_file_name").to_sym old_name = self.send(file_name) (self.send(key).styles.keys+[:original]).each do |style| path = self.send(key).path(style) self[file_name] = new_name new_path = self.send(key).path(style) new_path[0] = "" self[file_name] = old_name old_obj = self.send(key).s3_object(style.to_sym) new_obj = old_obj.move_to(new_path) end self.update_attribute(file_name, new_name) end 

Para usar: Model.find (#). Rename (: avatar, “test.jpg”)

Me gustaría donar mi solución de “movimiento seguro” que no se basa en ninguna API privada y protege contra la pérdida de datos debido a una falla de la red:

Primero, obtenemos los caminos antiguos y nuevos para cada estilo:

 styles = file.styles.keys+[:original] old_style2key = Hash[ styles.collect{|s| [s,file.path(s).sub(%r{\A/},'')]} ] self.file_file_name = new_filename new_style2key = Hash[ styles.collect{|s| [s,file.path(s).sub(%r{\A/},'')]} ] 

Luego, copiamos cada archivo a su nueva ruta. Dado que la ruta predeterminada incluye tanto el ID de objeto como el nombre de archivo, esto nunca puede chocar con la ruta de un archivo diferente. Pero esto fallará si intentamos cambiar el nombre sin cambiar el nombre:

 styles.each do |style| raise "same key" if old_style2key[style] == new_style2key[style] file.s3_bucket.objects[old_style2key[style]].copy_to(new_style2key[style]) end 

Ahora aplicamos el modelo actualizado a la base de datos:

 save! 

Es importante hacer esto después de crear los nuevos objetos S3, pero antes de eliminar los objetos antiguos S3. La mayoría de las otras soluciones en este hilo pueden llevar a una pérdida de datos si falla la actualización de la base de datos (por ejemplo, la división de la red con una mala sincronización), porque entonces el archivo estaría en una nueva ubicación S3 pero el DB aún apunta a la ubicación anterior. Es por eso que mi solución no elimina los objetos antiguos de S3 hasta después de que la actualización de la base de datos haya tenido éxito:

 styles.each do |style| file.s3_bucket.objects[old_style2key[style]].delete end 

Al igual que con la copia, no hay posibilidad de que eliminemos accidentalmente los datos de otro objeto de la base de datos, porque la identificación del objeto se incluye en la ruta. Entonces, a menos que cambie el nombre del mismo objeto de base de datos A-> B y B-> A al mismo tiempo (por ejemplo, 2 subprocesos), esta eliminación siempre será segura.

Para añadir a la respuesta de @Fotios:

Creo que es la mejor forma de crear un nombre de archivo personalizado, pero en caso de que desee un nombre de archivo basado en md5, puede usar la huella digital que ya está disponible en Paperclip.

Todo lo que tienes que hacer es poner esto en config / initializers / paperclip_defaults.rb

 Paperclip::Attachment.default_options.update({ # :url=>"/system/:class/:attachment/:id_partition/:style/:filename" :url=>"/system/:class/:attachment/:style/:fingerprint.:extension" }) 

No hay necesidad de establecer: ruta aquí, ya que por defecto está hecho de esa manera:

 :path=>":rails_root/public:url" 

No verifiqué si es necesario, pero en caso de que no te funcione, asegúrate de que tu modelo pueda guardar huellas dactilares en la base de datos -> aquí

Un consejo más que me parece útil es usar la consola de Rails para comprobar cómo funciona:

 $ rails c --sandbox > Paperclip::Attachment.default_options .. > s = User.create(:avatar => File.open('/foo/bar.jpg', 'rb')) .. > s.avatar.path => "/home/groovy_user/rails_projectes/funky_app/public/system/users/avatars/original/49332b697a83d53d3f3b5bebce7548ea.jpg" > s.avatar.url => "/system/users/avatars/original/49332b697a83d53d3f3b5bebce7548ea.jpg?1387099146" 

La siguiente migración me solucionó el problema.

Renombrando avatar a photo :

 class RenamePhotoColumnFromUsers < ActiveRecord::Migration def up add_attachment :users, :photo # Add `avatar` method (from Paperclip) temporarily, because it has been deleted from the model User.has_attached_file :avatar, styles: { medium: '300x300#', thumb: '100x100#' } User.validates_attachment_content_type :avatar, content_type: %r{\Aimage\/.*\Z} # Copy `avatar` attachment to `photo` in S3, then delete `avatar` User.where.not(avatar_file_name: nil).each do |user| say "Updating #{user.email}..." user.update photo: user.avatar user.update avatar: nil end remove_attachment :users, :avatar end def down raise ActiveRecord::IrreversibleMigration end end 

Espero eso ayude 🙂

Otra opción está configurada por defecto, trabajar para todas las cargas.

Este ejemplo cambia el archivo de nombre a ‘nombre por defecto’ para web, ejemplo: test áé.jpg a test_ae.jpg

helper / application_helper.rb

 def sanitize_filename(filename) fn = filename.split /(?<=.)\.(?=[^.])(?!.*\.[^.])/m fn[0] = fn[0].parameterize return fn.join '.' end 

Crear config / initializers / paperclip_defaults.rb

 include ApplicationHelper Paperclip::Attachment.default_options.update({ :path => ":rails_root/public/system/:class/:attachment/:id/:style/:parameterize_file_name", :url => "/system/:class/:attachment/:id/:style/:parameterize_file_name", }) Paperclip.interpolates :parameterize_file_name do |attachment, style| sanitize_filename(attachment.original_filename) end 

Necesita reiniciar, después de poner este código