CarrierWave: Создайте одно и то же уникальное имя файла для всех версионных файлов

Прежде чем вдаваться в подробности, я сразу перейду к делу: придумал ли кто-нибудь способ заставить Carrierwave сохранять файлы с их именами в виде меток времени или любой произвольной строки, уникальной для каждого файла?

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

Теперь, когда вы переопределяете метод store_dir своего загрузчика, чтобы он выглядел примерно так:

def store_dir
  "uploads/#{model.class.to_s.underscore}/#{mounted_as}"
end

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

Есть только одна проблема. Конфликты файлов. Если вы загрузите файл delicious_cake.jpg дважды, второй будет перезаписывать первый, даже если это две разные картинки вкусного торта! Очевидно, почему метод store_dir имеет дополнительный / # {model.id} , прикрепленный к концу возвращаемого значения.

Итак, что делать? Прочитав немного, я обнаружил, что в сгенерированном файле загрузчика есть очевидное решение, закомментированное.

# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
#   "something.jpg" if original_filename
# end

После небольшого поиска я нашел человека, который сделал следующее

def filename
  @name ||= "#{secure_token}.#{file.extension}" if original_filename
end

Это заставило меня задуматься, почему бы просто не сделать this

def filename
  @name ||= "#{(Time.now.to_i.to_s + Time.now.usec.to_s).ljust(16, '0')}#{File.extname(original_filename)}"
end

Вот когда все ужасно сломалось. Проблема в том, что filename , по-видимому, вызывается для каждой версии файла, поэтому мы получаем имена файлов, такие как 1312335603175322.jpg и thumb_1312335603195323.jpg. Заметили небольшую разницу? Каждое имя файла основано на времени, когда filename был вызван для этой конкретной версии. Это совсем не годится.

В следующий раз я устал использовать model.created_at в качестве основы для временной метки. Только одна проблема, которая возвращает nil для первой версии, поскольку она еще не была помещена в базу данных.

После некоторых размышлений я решил попробовать следующее в моем контроллере изображений.

def create
  if params[:picture] and params[:picture][:image]
    params[:picture][:image].original_filename = "#{(Time.now.to_i.to_s + Time.now.usec.to_s).ljust(16, '0')}#{File.extname(params[:picture][:image].original_filename)}"
  end
  ...

Это переопределяет свойство original_filename еще до того, как Carrierwave даже доберется до него, сделав его отметкой времени. Он делает именно то, что я хочу. Исходная версия файла имеет имя вроде 1312332906940106.jpg, а версия эскиза (или любая другая версия) заканчивается именем thumb_1312332906940106.jpg.

Но это похоже на ужасный взлом. Это должно быть частью модели или, еще лучше, частью загрузчика, установленного на модели.

Итак, мой вопрос: есть ли лучший способ добиться этого? Пропустил ли я что-то важное с Carrierwave, что упростило это? Есть ли не такой очевидный, но более чистый способ сделать это? Рабочий код - это хорошо, но рабочий код, который не плохо пахнет, лучше.

20
задан iwasrobbed 3 August 2011 в 13:25
поделиться