Изображения являются важной частью любого приложения. От социальной сети до простого баг-трекера изображения играют важную роль. Тем не менее, управление изображениями не является тривиальной задачей и требует много планирования заранее.
В этой статье позвольте мне продемонстрировать, как этого добиться в Rails. Я собираюсь показать вам, как обрабатывать ваши изображения и создавать несколько версий в бэкэнде. Мы также увидим, как повысить производительность страницы, сжимая эти изображения без потери качества.
Начиная
Примеры в этом руководстве работают на Rails 4.2 с базой данных MongoDb и HAML для визуализации представлений. Однако используемые здесь фрагменты должны быть совместимы с любой версией Rails, хотя и с небольшими отличиями в конфигурации.
Настройка сцены
ImageMagick — это библиотека для обработки изображений в системах POSIX. Если в вашей системе не установлен ImageMagick, его можно установить с помощью диспетчера пакетов для вашей ОС. На Ubuntu:
sudo apt-get -y install imagemagick
sudo apt-get -y install libmagic-dev
sudo apt-get -y install libmagickwand-dev
В Mac OS X я рекомендую использовать Homebrew:
brew install imagemagick
Теперь нам нужен адаптер Ruby для подключения к собственной библиотеке ImageMagick. Я лично предпочитаю MiniMagick, так как он легкий и выполняет практически все, что требуется типичному приложению:
# Gemfile
gem 'mini_magick'
Детская площадка
Давайте поиграем с некоторыми функциями MiniMagick, прежде чем строить что-то серьезное. Откройте консоль Rails ( rails c
# Open an image from a website
image = MiniMagick::Image.open("https://s3.amazonaws.com/StartupStockPhotos/20140808_StartupStockPhotos/85.jpg")
# Get the Image's width
image.width # 4928
# Get the image's height
image.height #3264
Боже мой, это огромно. Давайте посмотрим, сможем ли мы изменить его размер под наш iPad:
image.resize "2048x1536"
# Now get the image's new width and height
p "Width is => #{image.width} and height is => #{image.height}"
Ну, вот и все. Подождите секунду, где хранится этот измененный файл?
image.path # temp path
Манипулирующее изображение сохраняется в временной папке и будет смыто. Чтобы сохранить его на диске, просто вызовите метод write:
image.write "public/uploads/test.jpg"
Преобразование изображения
Одна из самых частых операций, которую вы выполняете, — это преобразование изображений в разные форматы MiniMagick делает это очень просто:
image.format "png"
image.write "public/uploads/test.png"
Схожу с ума
Вы также можете объединить несколько операций в одном блоке:
image.combine_options do |i|
i.resize "2048x1536"
i.flip
i.rotate "-45"
i.blur "0x15"
end
image.write "public/uploads/blur.png"
![Some weird result](blur.png)
Хорошо, я пошел немного за борт здесь. В свою защиту я просто пытаюсь показать все классные вещи, которые вы можете сделать с MiniMagick :).
Теперь давайте посмотрим, как мы можем связать это с нашим приложением Rails.
Загрузка файлов
Carrierwave — замечательная жемчужина, которая упрощает загрузку файлов в Ruby. Он также хорошо взаимодействует с MiniMagick, делая нашу жизнь намного проще.
# Gemfile
gem 'carrierwave'
gem 'carrierwave-mongoid', :require => 'carrierwave/mongoid'
ПРИМЕЧАНИЕ. Если вы используете ActiveRecord или DataMapper, конфигурации будут немного отличаться, и официальная документация Carrierwave покажет вам путь.
Пакет, чтобы получить все эти драгоценные камни:
bundle install
Создайте наш первый загрузчик:
#app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
include CarrierWave::MiniMagick
# Choose what kind of storage to use for this Uploader:
storage :file
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/images"
end
end
Код здесь не требует пояснений. storage :file
store_dir
Поскольку файлы отправляются через Интернет, рекомендуется всегда фильтровать входящие файлы:
# app/uploaders/image_uploader.rb
...
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
%w(jpg jpeg png gif)
end
...
Этот фрагмент отфильтровывает типы файлов, отличные от указанных здесь. Это ни в коем случае не является защитой от дурака, но оно служит фильтром первого уровня против любой атаки препятствий.
Установите этот загрузчик на нашу модель изображения:
# app/models/image.rb
class Image
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
include Mongoid::Attributes::Dynamic
include Rails.application.routes.url_helpers
mount_uploader :media, ImageUploader, mount_on: :media_filename
end
Отредактируйте файл image_uploader.rb для обработки загруженного изображения:
# app/uploaders/image_uploader.rb
#.....
process :resize_to_fill => [200, 200]
process :convert => 'png'
#.....
Попробуйте создать новый образ из консоли Rails:
media = File.open("/Users/test/Desktop/image/jpg")
img = Image.new(:media => media)
img.save
Загруженное изображение доступно в store_dir
Однако загруженное изображение немедленно обрабатывается и перезаписывается изображением 200 × 200. У нас не будет копии исходного файла для любых будущих изменений. Чтобы избежать этого, создайте несколько версий файла.
# app/uploaders/image_uploader.rb
#.....
version :thumb do
process :resize_to_fit => [100, 100]
process :convert => 'jpg'
end
version :cover do
process :resize_to_fit => [240, 180]
process :convert => 'jpg'
end
#.....
Это создает 2 новые версии вместе с исходным изображением. Проверьте версии, созданные Carrierwave:
img.media.versions[:thumb] # returns the thumb image instance
img.media.versions[:cover] # returns the cover image instance
Вы заметили, что эти изображения генерируются мгновенно? Это означает, что преобразование изображения происходит в том же потоке, и выполнение блокируется до его завершения. В производственном приложении нежелательно создавать несколько версий изображения в одном потоке. Вместо этого мы должны обращаться с этим условно.
# app/uploaders/image_uploader/rb
#....
version :cover, :if => :is_live? do
process :resize_to_fit => [240, 180]
process :convert => 'jpg'
end
def is_live?(img = nil)
@is_live
end
def is_live=(value)
@is_live = value
end
#....
Теперь, когда мы пытаемся создать новое изображение, кавер-версия не будет сгенерирована. Мы можем вызвать это вручную, просто запустив:
img.media.is_live = true
img.save
img.media.recreate_versions! :cover
Этот код также выполняется на переднем плане и является блокирующей операцией, но, по крайней мере, он откладывается до последнего возможного момента. Мы можем сделать это еще дальше, запустив это в фоновом режиме с помощью Resque:
# lib/resque/image_queue.rb
class ImageQueue
@queue = :image_queue
def self.perform(image_id)
image = Image.find image_id
img.media.is_live= true
img.save
img.media.recreate_versions! :cover
end
end
и поставьте в очередь:
Resque.enqueue(ImageQueue, img.id.to_s)
Улучшение производительности
Изображения тяжелые и имеют тенденцию замедлять работу сайта. Один из способов уменьшить вес страницы — сжать эти изображения. Carrierwave Image Optimizer помогает нам сжимать наши изображения на лету без каких-либо хлопот.
Добавьте это в свой Gemfile:
gem 'carrierwave-imageoptimizer'
И отредактируйте image_uploader
# app/uploaders/image_uploader.rd
#.....
include CarrierWave::ImageOptimizer
process :optimize
process :quality => 100
#....
Это сжимает все изображения без каких-либо визуальных потерь. То, как это работает, заключается в удалении всей метаинформации об изображении. В среднем это уменьшает размер примерно на 20-30%.
Завершение
Обработка изображений — это огромная вертикаль, и мы едва поцарапали поверхность. С его помощью мы можем создать так много классных вещей. Надеюсь, я заинтересовался этой статьей. Пожалуйста, поделитесь своими мыслями в комментариях.