Прежде чем я углубится в подробности, я пойду прямо: кто-нибудь понял, как заставить Carrierwave сохранять файлы с их именами как временную метку или любую произвольную строку, уникальную для каждого файла?
По умолчанию Carrierwave сохраняет каждый файл и его альтернативные версии в своем собственном каталоге (названном после идентификационного номера модели). Я не поклонник этого, потому что вместо одного каталога с 1000, ради использования большого круглого числа, файлы (в моем случае фотографии) в нем мы получаем один каталог с 1000 подкаталогами с одним или двумя файлами. Тьфу.
Теперь, когда вы переопределите метод Uploader 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
Это заставило меня задуматься, почему бы просто не сделать это
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, что делает это проще? Есть ли не столь очевидный, но более чистый способ обойти это? Рабочий код хорош, но рабочий код, который плохо пахнет, лучше.