Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby on Rails

Загрузка файлов с помощью Rails и Shrine

by
Difficulty:IntermediateLength:LongLanguages:

Russian (Pусский) translation by Marat Amerov (you can also view the original English article)

Есть достаточно гемов, которые предназначены для загрузки файлов, такие как CarrierWave, Paperclip и Dragonfly, несколько из них, которые стоит упомянуть. У всех есть свои особенности, и  вероятно, Вы уже использовали хотя бы один из этих гемов.

Однако сегодня я хочу представить относительно новое, но очень крутое решение, называемое Shrine, созданное Янко Мароничем. В отличие от некоторых других подобных гемов, он имеет модульный подход, что означает, что каждая функция упакована в виде модуля (или плагина в терминологии Shrine). Хотите поддержку валидации? Добавьте плагин. Желаете сделать некоторую обработку файлов? Добавить плагин! Мне очень нравится этот подход, поскольку он позволяет вам легко контролировать, какие функции будут доступны для конкретной модели.

В этой статье я расскажу вам, как:

  • Интегрировать Shrine в Rails приложение
  • Конфигурировать его (глобально и для каждой модели)
  • Добавить возможность загрузки файлов
  • Обработка файлов
  • Добавить правила валидации
  • Хранить дополнительные метаданные и использовать хранилище файлов с Amazon S3

Исходный код этой статьи доступен на GitHub.

Рабочее демо можно найти здесь.

Интеграция Shrine

Чтобы начать, создайте новое Rails приложение без тестового фреймворка по умолчанию:

Я буду использовать Rails 5 для этой демонстрации, но большинство концепций подходят к версиям 3 и 4.

Добавьте гем Shrine в свой Gemfile:

Затем выполните:

Теперь нам понадобится модель, которую я собираюсь назвать Photo. Shrine хранит всю связанную с файлами информацию в специальном текстовом столбце, заканчивающемся суффиксом _data. Создайте и примените соответствующую миграцию:

Обратите внимание, что для старых версий Rails последняя команда должна быть:

Параметры конфигурации для Shrine могут быть установлены как глобально, так и для каждой модели. Глобальные настройки выполняются, внутри файла инициализации. Там я собираюсь подключить необходимые файлы и плагины. В Shrine плагины используются для отделения частей функциональности в отдельные модули, что дает Вам полный контроль над всеми доступными функциями. Например, есть плагины для валидации, обработки изображений, кэширования вложений и т.д.

Пока добавим два плагина: один для поддержки ActiveRecord и еще один для настройки логирования. Они будут включены глобально. Кроме того, настроим файловое хранилище:

config/initializers/shrine.rb

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

Все загруженные файлы будут храниться в каталоге public/uploads. Я не хочу отслеживать эти файлы в Git, поэтому исключу эту папку:

.gitignore

Теперь создайте специальный класс загрузчик, в котором будут размещаться конфиги для модели. Пока этот класс будет пустым:

models/image_uploader.rb

Наконец, включите этот класс в модель Photo:

models/photo.rb

[:Image] добавляет виртуальный атрибут, который будет использоваться при создании формы. Вышеупомянутая строка может быть переписана так:

Хорошо! Теперь модель оснащена функциональностью Shrine, и мы можем перейти к следующему шагу.

Контроллер, представление и роутинг

Для целей этой демонстрации нам нужен только один контроллер для управления фотографиями. Страница index будет служить в качестве корня:

pages_controller.rb

Представление:

views/photos/index.html.erb

Для рендеринга массива @photos требуется партиал:

views/photos/_photo.html.erb

image_data? Это метод, предоставленный Shrine, который проверяет, имеет ли запись изображение.

image_url - еще один метод Shrine, который просто возвращает ссылку исходного изображения. Конечно, гораздо лучше отобразить небольшое уменьшенное изображение, но мы позаботимся об этом позже.

Добавим все необходимые роуты:

config/routes.rb

Фундамент создан, теперь мы можем перейти к интересной части!

Загрузка файлов

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

photos_controller.rb

Единственная проблема заключается в том, что для strong parameters, Вы должны разрешать виртуальный атрибут image, а не image_data.

photos_controller.rb

Создадим представление new:

views/photos/new.html.erb

Партиал формы также тривиален:

views/photos/_form.html.erb

Еще раз отметим, что мы используем атрибут image, а не image_data.

Наконец, добавим еще один партиал для отображения ошибок:

views/shared/_errors.html.erb

Это почти все - можете начать загрузку изображений прямо сейчас.

Валидация

Конечно же, для завершения демонстрационного приложения нужно сделать гораздо больше работы. Основная проблема заключается в том, что пользователи могут загружать абсолютно любой тип файла с любым размером, что не очень хорошо. Поэтому, добавим еще один плагин для поддержки валидации:

config/inititalizers/shrine.rb

Конфигурация логики валидации для ImageUploader:

models/image_uploader.rb

Я разрешаю загружать только изображения JPG и PNG менее 1 МБ. Поправьте эти правила в соответствии вашим требованиям.

MIME типы

Еще одна важная вещь, которую следует отметить, заключается в том, что по умолчанию Shrine будет определять MIME-тип файла, используя HTTP-заголовок Content-Type. Этот заголовок передается браузером и устанавливается только на основе расширения файла, что не всегда желательно.

Если вы хотите определить MIME-тип на основе содержимого файла, используйте плагин define_mime_type. Я включу его в класс загрузчика, так как другие модели могут и не требовать этого функционала:

models/image_uploader.rb

Этот плагин по умолчанию будет использовать утилиту file из ОС Linux.

Кэширование прикрепленных изображений

В настоящее время, когда пользователь отправляет форму с неверными данными, форма снова будет отображаться с ошибками, представленными выше. Однако, проблема в том, что прикрепленное изображение будет потеряно, и пользователю нужно будет выбрать его еще раз. Это очень легко исправить, используя еще один плагин cached_attachment_data:

models/image_uploader.rb

Теперь, просто добавьте скрытое поле в вашу форму.

views/photos/_form.html.erb

Редактирование фотографии

Теперь изображения могут быть загружены, но нет возможности редактировать их, поэтому давайте сразу исправим это. Действия соответствующего контроллера довольно тривиальны:

photos_controller.rb

Будет использоваться тот же партиал _form:

views/photos/edit.html.erb

Удобно, но недостаточно: пользователи по-прежнему не могут удалить загруженное изображение. Для того, чтобы исправить это, думаю, что нам понадобится еще один плагин:

models/image_uploader.rb

Он использует виртуальный атрибут: remove_image, поэтому разрешим его внутри контроллера:

photos_controller.rb

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

views/photos/_form.html.erb

Создание эскиза изображения

В настоящее время мы показываем оригинальные изображения, которые не подходят для предварительного просмотра: фотографии могут быть большими и занимать слишком много места. Конечно, Вы могли бы просто использовать CSS атрибуты width и height, но это тоже плохая идея. Как видите, даже если изображение установлено как уменьшенное с помощью стилей, пользователю все равно нужно будет загрузить исходный файл, который может быть довольно большим.

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

Image_processing - это особый гем, созданный автором Shrine. В нем представлены некоторые вспомогательные методы для управления изображениями на высоком уровне. Этот гем, в свою очередь, полагается на mini_magick - оболочку Ruby для ImageMagick. Как Вы уже догадались, нам понадобится ImageMagick в системе, чтобы запустить этот пример.

Установим эти гемы:

Теперь включите плагины вместе с их зависимостями:

models/image_uploader.rb

Processing - это плагин для манипулирования изображением (например, сжатие, поворот, преобразование в другой формат и т. д.). Versions, в свою очередь, позволяют нам иметь изображение в разных вариантах. Для этой демонстрации будут сохранены две версии: "original" и "thumb"  (размер изменён на 300x300).

Вот код для обработки изображения и хранения его двух версий:

models/image_uploader.rb

resize_to_limit! Это метод, предоставляемый гемом обработки изображений. Он просто уменьшает изображение до 300x300, если оно больше, и ничего не делает, если оно меньше. Кроме того, он сохраняет исходное соотношение сторон.

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

views/photos/_photo.html.erb

То же самое можно сделать и в форме:

views/photos/_form.html.erb

Чтобы автоматически удалить обработанные файлы после завершения загрузки, можно добавить плагин delete_raw:

models/image_uploader.rb

Метаданные изображения

Помимо рендеринга изображения, также можно получить его метаданные. Давайте, например, покажем размер оригинальной фотографии и MIME-тип:

views/photos/_photo.html.erb

Как насчет его размеров? К сожалению, они не сохраняются по умолчанию, но это возможно с помощью плагина store_dimensions.

Размеры изображения

Плагин store_dimensions опирается на гем fastimage, поэтому подключим его сейчас:

Не забудем запустить:

Теперь просто включим плагин:

models/image_uploader.rb

И покажем размеры с помощью методов width и height:

views/photos/_photo.html.erb

Кроме того, существует доступный метод dimensions, который возвращает массив, содержащий ширину и высоту (например, [500, 750]).

Перемещение в облако

Разработчики часто выбирают облачные сервисы для размещения загруженных файлов, и Shrine действительно представляет такую ​​возможность. В этом разделе я покажу Вам, как загружать файлы на Amazon S3.

В качестве первого шага включите еще два гема в Gemfile:

aws-sdk требуется для работы с SDK S3, тогда как dotenv-rails будет использоваться для управления переменными окружения в процессе разработки.

Прежде чем продолжить, Вы должны получить пару ключей для доступа к S3 через API. Чтобы получить его, войдите в систему (или зарегистрируйтесь) в Amazon Web Services Console и перейдите к Security Credentials > Users. Создайте пользователя с правами для управления файлами на S3. Вот простая политика, предоставляющая полного доступа к S3:

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

Затем создайте S3 bucket для размещения ваших файлов и добавьте файл в корень проекта для размещения вашей конфигурации:

.env

Никогда не публикуйте этот файл и исключите его из Git:

.gitignore

Теперь изменим глобальную конфигурацию Shrine и добавим новое хранилище:

config/initializers/shrine.rb

Вот именно! Никакие изменения не должны быть внесены в другие части приложения, теперь Вы можете сразу проверить это новое хранилище. Если вы получаете ошибки от S3, связанные с неправильными ключами, убедитесь, что Вы точно скопировали ключи, без каких-либо конечных пробелов и невидимых специальных символов.

Заключение

Мы подошли к концу статьи. Надеюсь, к настоящему моменту Вы чувствуете себя уверенно в использовании Shrine и готовы использовать его в одном из своих проектов. Мы обсудили многие особенности этого гема, но их еще больше, например, способность сохранять дополнительный контекст вместе с файлами и механизм прямой загрузки.

Поэтому просмотрите документацию Shrine и официальный сайт, в котором подробно описаны все доступные плагины. Если у вас остались другие вопросы об геме, не стесняйтесь задавать их. Я благодарю вас за то, что оставались со мной, скоро увидимся!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.