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