Как переименовать файл после того, как он был загружен и сохранен? Моя проблема в том, что мне нужно автоматически анализировать информацию о файлах, чтобы придумать имя файла, файл должен быть сохранен как с моим приложением, но я не могу получить доступ к информации, необходимой для генерации имени файла, до записи для модель была сохранена.
Паперклипы переименовывают файлы после их сохранения
Ответ 1
Если, например, ваша модель имеет изображение атрибута:
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
Ответ 2
Вы проверили интерполяции paperclip?
Если это то, что вы можете определить в контроллере (до его сохранения), вы можете использовать комбинацию контроллера, модели и интерполяции, чтобы решить вашу проблему.
У меня есть этот пример, где я хочу назвать файл на основе хеша 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'
Ответ 3
Чтобы добавить к ответу @Voyta, если вы используете S3 с paperclip:
(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)
Ответ 4
Изображения моего аватара названы с помощью slug пользователя, если они меняют свои имена, мне тоже нужно переименовать изображения.
То, как я переименовываю изображения моего аватара с помощью 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
Ответ 5
И чтобы добавить еще один ответ, вот полный метод, который я использую для переименования 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" )
Ответ 6
Я хочу пожертвовать свое решение "безопасного перехода", которое не полагается на какой-либо частный 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 потока), это удаление всегда будет безопасным.
Ответ 7
Чтобы добавить к @Fotios ответ:
его лучший способ создать собственное имя файла, но если вам нужно имя файла на основе md5, вы можете использовать отпечаток пальца, который уже доступен в папке.
Все, что вам нужно сделать, это поместить это в 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"
Ответ 8
Следующая миграция решила проблему для меня.
Переименование 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
Надеюсь, это поможет:)
Ответ 9
Другой параметр установлен на значение по умолчанию, работает для всей загрузки.
В этом примере измените файл имени на "имя по умолчанию" для сети, например: 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
Нужно перезапустить, после этого кода