Advertisement
  1. Code
  2. Ruby on Rails

Загрузка файлов с Rails и Dragonfly

by
Read Time:14 minsLanguages:

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

Некоторое время назад я написал статью "Загрузка файлов с Rails и Shrine", в которой объяснялось, как добавить возможность загрузки файлов в ваше приложение Rails с использованием библиотеки Shrine. Однако, существует еще несколько похожих решений, и одно из моих любимых — Dragonfly, простая и удобная в использовании библиотека для Rails и Rack, созданная Марком Эвансом.

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

В данной статье я объясню процесс внедрения библиотеки Dragonfly и использование её основного функционала. Вы научитесь:

  • интегрировать Dragonfly в ваше приложение;
  • настраивать модели для работы с Dragonfly;
  • внедрять базовый механизм загрузки;
  • внедрять правила валидации;
  • создавать миниатюры;
  • осуществлять обработку файлов;
  • сохранять метаданные загруженных файлов;
  • подготавливать приложение для развертывания;

Чтобы сделать процесс более интересным, мы создадим маленькое музыкальное приложение. В нём будут отображаться альбомы и песни, которые можно проигрывать на сайте.

Исходный код можно найти на сайте GitHub. Также вы можете ознакомиться с демо-версией приложения.

Отображение и управление альбомами

Для начала создадим новое приложение Rails без стандартного набора тестирования:

В этой статье я буду использовать Rails 5, но большая часть описанных приёмов применимы и к более старым версиям.

Создание модели, контроллера и маршрутов

Наш небольшой музыкальный сайт будет содержать две модели: Album и Song. Давайте создадим первую модель со следующими полями:

  • title (string) — содержит название альбома
  • singer (string) — исполнитель
  • image_uid (string) — специальное поле для хранения обложки альбома Вы можете выбрать для этого поля любое понравившееся название, но в нем обязательно должен присутствовать суффикс _uid, как указано в документации Dragonfly.

Создайте и примените соответствующую миграцию:

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

albums_controller.rb

И, наконец, добавим маршруты:

config/routes.rb

Интегрирование библиотеки Dragonfly

Пришло время Dragonfly выйти на сцену. Для начала добавим библиотеку в Gemfile:

Gemfile

Запускаем:

Последняя команда создаст файл инициализации под названием dragonfly.rb со стандартными настройками. Пока что мы не будем его редактировать, но вы можете прочитать о доступных опциях на официальном сайте Dragonfly.

Следующий важный этап — добавление методов Dragonfly к нашей модели. Это осуществляется с помощью dragonfly_accessor:

models/album.rb

Обратите внимание, что я пишу :image — это напрямую связано с колонкой image_uid, которую мы создали в предыдущей части. Например, если вы назвали свою колонку photo_uid, то методу dragonfly_accessor необходимо передать :photo в качестве аргумента.

Если вы используете версию Rails 4 или 5, то следующий важный шаг — пометить поле :image (не :image_uid!) в контроллере как разрешенное.

albums_controller.rb

Вот в принципе и всё, мы можем переходить к созданию представлений и начинать загрузку наших файлов!

Создание представлений

Начнем с представления index:

views/albums/index.html.erb

Теперь частичное представление:

views/albums/_album.html.erb

В данном листинге необходимо обратить внимание на два метода Dragonfly:

  • album.image.url возвращает путь к изображению;
  • album.image_stored? сообщает, имеет ли запись загруженное изображение.

Теперь добавим страницы для добавления и редактирования альбомов:

views/albums/new.html.erb

views/albums/edit.html.erb

views/albums/_form.html.erb

Эта форма очень простая, но еще раз обратите внимание, что мы пишем :image, а не :image_uid при отображении поля для выбора файла.

Теперь вы можете запустить сервер и протестировать функцию загрузки!

Удаление изображений

Итак, пользователи могут создавать и редактировать альбомы, но есть одна проблема: у них нет возможности удалять изображение, только заменить его другим. К счастью, это легко исправить, добавив флажок "удалить изображение":

views/albums/_form.html.erb

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

Единственное, что нужно сделать, это разрешить атрибут remove_image в вашем контроллере:

albums_controller.rb

Добавление валидаций

На данном этапе всё замечательно работает, но мы не проверяем данные, введенные пользователем, что не очень хорошо. Поэтому давайте добавим валидации для модели Album:

models/album.rb

validates_property — это метод Dragonfly, который может проверять различные характеристики вашего вложения: расширение файла, тип MIME, размер и так далее.

Теперь давайте создадим базовое частичное представление для отображения найденных ошибок:

views/shared/_errors.html.erb

Используйте в форме частичное представление:

views/albums/_form.html.erb

Стилизуйте поля с ошибками, чтобы они визуально выделялись:

stylesheets/application.scss

Сохранение изображения между запросами

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

Dragonfly помогает устранить эту проблему путем использования скрытого поля retained_*:

views/albums/_form.html.erb

Не забудьте также разрешить это поле:

albums_controller.rb

Теперь изображение сохраняется между запросами! Единственная небольшая проблема: поле для выбора файла всё равно будет содержать текст "выберите файл", но это можно исправить с помощью нескольких стилей и JavaScript.

Обработка изображений

Генерация миниатюр

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

Другим решением (зачастую более предпочтительным) является генерация миниатюр на сервере с заранее заданным разрешением.  Это очень легко сделать с помощью Dragonfly:

views/albums/_album.html.erb

250x250 — это, конечно, разрешение, а # — это геометрия, которая обозначает "центрировать, масштабировать и обрезать по необходимости, сохраняя изначальное соотношение сторон". Вы можете найти информацию о других геометриях на сайте Dragonfly.

Метод thumb поддерживается ImageMagick — замечательным решением для создания и манипулирования изображениями. Таким образом, для того, чтобы запустить демонстрационное приложение у себя на компьютере, вам потребуется установить ImageMagick (поддерживает все основные платформы).

Поддержка ImageMagick по умолчанию включена в файле инициализации Dragonfly.

config/initializers/dragonfly.rb

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

Первый вариант подразумевает добавление новой колонки для хранения миниатюры и изменение метода dragonfly_accessor. Создайте и примените новую миграцию:

Теперь измените модель:

models/album.rb

Обратите внимание, что первый вызов dragonfly_accessor принимает блок, который генерирует миниатюру и копирует ее в image_thumb. Теперь просто используйте метод image_thumb в ваших представлениях:

views/albums/_album.html.erb

Это решение самое простое, но оно не рекомендуется в официальной документации и, что ещё хуже, на момент написания этой статьи оно не работало с полями retained_*.

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

Таблица thumbs будет хранить ваши миниатюры, однако их генерирование будет производиться по требованию. Чтобы это происходило, нам потребуется переопределить метод url в файле инициализации Dragonfly:

config/initializers/dragonfly.rb

Теперь добавьте новый альбом и перейдите на главную страницу. При первом переходе в журнале вы найдете следующую запись:

Фактически, это значит, что ImageMagick генерирует для нас миниатюру. . Однако, если вы обновите страницу, эта строка больше не появится: это значит, что миниатюра была закэширована! Более подробно об этой функции вы можете прочитать на официальном сайте Dragonfly.

Другие возможности обработки

Фактически, после загрузки изображений вы можете производить с ними любые манипуляции. Это можно сделать в функции обратного вызова after_assign. К примеру, конвертируем все наши изображения в формат JPEG с качеством в 90% от исходного:

Есть множество других действий, которые вы можете выполнить: поворачивать и обрезать изображения, сконвертировать в другой формат, добавить на изображение текст, скомбинировать с другим изображением (например, чтобы добавить водяной знак) и так далее. Для ознакомления с другими примерами обратитесь к разделу ImageMagick на сайте Dragonfly.

Загрузка и управление треками

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

Добавьте методы Dragonfly, как мы делали это для модели Album.

models/song.rb

Не забудьте добавить отношение has_many:

models/album.rb

Добавьте новые маршруты. Песня всегда существует в контексте альбома, так что я сделаю эти маршруты вложенными:

config/routes.rb

Создайте очень простой контроллер (еще раз, не забудьте разрешить поле track):

songs_controller.rb

Отобразите песни и ссылку для добавления новой композиции:

views/albums/show.html.erb

Создайте форму:

views/songs/new.html.erb

Наконец, добавьте частичное представление _song:

views/songs/_song.html.erb

Здесь я использую тег HTML5 audio, который не будет работать в старых версиях браузеров. Так что, если вы хотите осуществлять поддержку таких браузеров, используйте polyfill.

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

Сохранение метаданных

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

Однако, когда мы работаем с аудио или видео, всё немного усложняется, потому что для извлечения метаданных необходима специальная библиотека streamio-ffmpeg. Эта библиотека, в свою очередь, основываеся на решении FFmpeg, так что предварительно вам потребуется установить его на свой компьютер.

Добавьте streamio-ffmpeg в Gemfile:

Gemfile

Установите:

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

models/song.rb

Заметьте, что здесь я использую метод path, а не url, потому что на данном этапе мы работаем с временным файлом. Дальше мы просто извлекаем длительность трека (преобразовывая в минуты и секунды с ведущими нулями) и битрейт (преобразовывая в килобайты в секунду).

Наконец, выводим метаданные в представлении:

views/songs/_song.html.erb

Если вы проверите содержимое папки public/system/dragonfly (путь, где по умолчанию хранятся загрузки), то увидите файлы с расширением .yml — в них в формате YAML хранится вся метаинформация.

Разворачиваем приложение на Heroku

Последняя тема, которую мы сегодня рассмотрим — это подготовка вашего приложения к разворачиванию в облачную платформу Heroku. Основная проблема состоит в том, что Heroku не позволяет хранить пользовательские файлы (например, загрузки), что вынуждает нас пользоваться такими сервисами облачного хранилища, как Amazon S3. К счастью, Dragonfly позволяет легко добавить поддержку таких сервисов.

Всё, что вам нужно, это зарегистрировать на AWS новый аккаунт (если у вас его еще нет), создать пользователя с правом доступа к корзинам S3 и сохранить ключевую пару этого пользователя в надёжном месте. Вы можете использовать корневую ключевую пару, но делать это не рекомендуется. Наконец, создайте корзину S3.

Возвращаемся к нашему приложению Rails и добавляем новую библиотеку:

Gemfile

Установите его:

Затем скорректируйте настройки Dragonfly так, чтобы в продукционной среде использовался S3:

config/initializers/dragonfly.rb

Чтобы добавить переменные ENV на Heroku, используйте следующую команду:

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

Другая небольшая проблема, с которой я столкнулся при развёртывании приложения на Heroku — отсутствие FFMpeg. Дело в том, что когда создаётся новое приложение Heroku, оно уже имеет ряд сервисов, которые обычно используются (например, ImageMagick доступен по умолчанию). Другие сервисы могут быть установлены как дополнения Heroku, либо в с помощью buildpack. Для того, чтобы добавить FFmpeg buildpack, выполните следующую команду:

Теперь всё готово и вы можете поделиться своим музыкальным приложением с миром!

Заключение

Это было довольно длинное путешествие, не правда ли? Сегодня мы обсуждали Dragonfly — решение для Rails, позволяющее внедрять возможность загрузки файлов. Мы узнали, как добавить Dragonfly в своё приложение и обсудили некоторые опции для настройки этой библиотеки. Кроме того, вы узнали, как генерировать миниатюры изображений, выполнять обработку файлов и сохранять метаданные. Кроме того, мы настроили поддержку Amazon S3 в Dragonfly и подготовили приложение для разворачивания в производственную среду.

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

Спасибо за внимание и до новых встреч!

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.