Скрепка переименовывает файлы после их сохранения - PullRequest
18 голосов
/ 25 апреля 2010

Как переименовать файл после того, как он был загружен и сохранен? Моя проблема в том, что мне нужно автоматически анализировать информацию о файлах, чтобы найти имя файла, файл должен быть сохранен, как и в моем приложении, но я не могу получить доступ к информации, необходимой для создания имени файла, до модель была сохранена.

Ответы [ 9 ]

24 голосов
/ 25 апреля 2010

Если, например, ваша модель имеет атрибут image:

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

По умолчанию файлы papepclip хранятся в /system/:attachment/:id/:style/:filename.

.

Итак, вы можете сделать это, переименовав каждый стиль, а затем изменив столбец image_file_name в базе данных.

(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
23 голосов
/ 14 мая 2010

Вы проверили интерполяции скрепки ?

Если это то, что вы можете выяснить в контроллере (до его сохранения), вы можете использовать комбинацию контроллера, модели и интерполяции для решения вашей проблемы.

У меня есть этот пример, где я хочу назвать файл на основе его хеша MD5.

В моем контроллере у меня есть:

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

У меня тогда есть config/initializers/paperclip.rb с:

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

Наконец, в моей модели у меня есть:

validates_attachment_presence :upload
has_attached_file :upload,
  :path => ':rails_root/public/files/:md5.:extension',
  :url => '/files/:md5.:extension'
18 голосов
/ 13 июня 2011

Чтобы добавить ответ @ Voyta, если вы используете S3 со скрепкой:

(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)
6 голосов
/ 13 февраля 2013

Мои аватарные изображения именуются слагом пользователя, если они меняют свои имена, я тоже должен переименовывать изображения.

Так я переименую свои аватары, используя S3 и скрепку.

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
4 голосов
/ 01 февраля 2012

И чтобы добавить еще один ответ, вот полный метод, который я использую для переименования 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

Для использования: Model.find (#). Rename (: avatar, "test.jpg")

2 голосов
/ 02 августа 2016

Я хотел бы пожертвовать свое решение «безопасного перемещения», которое не зависит от какого-либо частного API и защищает от потери данных из-за сбоя сети:

Сначала мы получаем старые и новые пути для каждого стиля:

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/},'')]} ]

Затем мы копируем каждый файл на новый путь. Поскольку путь по умолчанию включает в себя как идентификатор объекта, так и имя файла, он никогда не может конфликтовать с путем для другого файла. Но это не удастся, если мы попытаемся переименовать без изменения имени:

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

Теперь мы применяем обновленную модель к БД:

save!

Важно сделать это после того, как мы создадим новые объекты S3, но перед тем, как удалить старые объекты S3. Большинство других решений в этом потоке могут привести к потере данных в случае сбоя обновления базы данных (например, разделение сети с неправильной синхронизацией), поскольку тогда файл будет находиться в новом местоположении S3, но БД все еще будет указывать на старое местоположение. Вот почему мое решение не удаляет старые объекты S3 до тех пор, пока не удастся обновить БД:

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

Как и в случае с копией, мы не можем случайно удалить данные другого объекта базы данных, потому что идентификатор объекта включен в путь. Поэтому, если вы не переименуете один и тот же объект базы данных A-> B и B-> A в одно и то же время (например, 2 потока), это удаление всегда будет безопасным.

0 голосов
/ 08 марта 2017

Другой параметр установлен по умолчанию, работает для всех загрузки.

В этом примере изменение имени файла на «имя по умолчанию» для сети, например: test áé.jpg на test_ae.jpg

хелперов / application_helper.rb

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

Создать 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

Нужно перезагрузить, после того, как поставил этот код

0 голосов
/ 21 августа 2016

Следующая миграция решила проблему для меня.

Переименование avatar в 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

Надеюсь, это поможет:)

0 голосов
/ 15 декабря 2013

Добавить к ответу @ Фотиоса:

я думаю, это лучший способ создать произвольное имя файла, но если вы хотите, чтобы имя файла основывалось на md5, вы можете использовать отпечаток, который уже доступен в Paperclip.

Все, что вам нужно сделать, это поместить это в 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"
    })

Нет необходимости устанавливать: путь здесь, так как по умолчанию он сделан таким образом:

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

Я не проверял, если это необходимо, но в случае, если это не работает для вас, убедитесь, что ваша модель способна сохранять отпечатки пальцев в базе данных -> здесь

Еще один совет, который я считаю удобным, - это использовать консоль rails, чтобы проверить, как она работает:

$ 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" 
...