В моей предыдущей статье мы узнали, как включить загрузку файлов в Rails с помощью Refile . Сегодня мы рассмотрим другой камень загрузки файлов — Shrine . Shrine — еще один инструментарий для загрузки файлов в приложениях на Ruby.
В своем вступительном сообщении в блоге автор Shrine, Янко Марохнич, указывает, что Shrine находится под сильным влиянием как Refile, так и CarrierWave. Shrine использует систему плагинов для всего, поставляя с тоннами плагинов , некоторые из которых я буду использовать сегодня.
Мы будем использовать Shrine для создания приложения на Rails, в котором хранятся изображения и книги. Да, книги … вы можете показать своим друзьям свою последнюю коллекцию книг. Я надеюсь, что вы любите читать столько же, сколько и я. Давайте построим эту вещь и выпустим ее на всеобщее обозрение.
Установка ImageMagick
Для работы Shrine вам необходимо установить ImageMagick. В зависимости от вашей операционной системы, выполните одно из следующих действий.
Пользователи Mac:
brew install imagemagick
Пользователи Ubuntu:
sudo apt-get install imagemagick
Если ни одно из вышеперечисленного не работает для вас, то есть вы работаете в Windows или что-то еще, проверьте страницу установки на сайте ImageMagick. Существуют бинарные выпуски для Windows 7 и выше.
Генерация приложений Rails
Запустите команду, чтобы создать новое приложение:
rails new book-showcase
Мы собираемся использовать один контроллер
rails generate controller Books
Настройте соответствующие маршруты:
конфиг / routes.rb
Rails.application.routes.draw do resources :books root to: "books#index" end
Интегрировать Храм
Прежде чем мы приступим к созданию модели, давайте перетащим Shrine в приложение Rails. Откройте свой Gemfile и добавьте драгоценный камень Shrine и его зависимости.
Gemfile
# Shrine Dependencies gem 'fastimage' gem 'image_processing' gem 'mini_magick' gem 'shrine'
- fastimage используется для извлечения размеров изображения.
- image_processing включает в себя несколько помощников для использования ImageMagick
- mini_magick — это замена нехватки памяти для RMagick, Ruby-оболочки вокруг ImageMagick
Бегать
bundle install
Теперь давайте создадим нашу модель Book:
rails generate model Book
Откройте вашу миграцию и сделайте так:
xxx_create_books.rb
class CreateBooks < ActiveRecord::Migration def change create_table :books do |t| t.string :name t.text :image_data t.timestamps null: false end end end
Запустите миграцию
rake db:migrate
Нам нужно настроить модель с функциональностью Shrine. Создайте новый файл в папке вашей модели , назовите его image_uploader.rb :
touch app/models/image_uploader.rb
Вставьте следующее в файл, который вы только что создали:
app / models / image uploader.rb_
class ImageUploader < Shrine include ImageProcessing::MiniMagick plugin :activerecord plugin :determine_mime_type plugin :logging, logger: Rails.logger plugin :remove_attachment plugin :store_dimensions plugin :validation_helpers plugin :versions, names: [:original, :thumb] Attacher.validate do validate_max_size 2.megabytes, message: 'is too large (max is 2 MB)' validate_mime_type_inclusion ['image/jpg', 'image/jpeg', 'image/png', 'image/gif'] end def process(io, context) case context[:phase] when :store thumb = resize_to_limit!(io.download, 200, 200) { original: io, thumb: thumb } end end end
Как уже упоминалось, Shrine предоставляет нам различные плагины для разных функций. Вот плагины, включенные в наш загрузчик и их функции:
-
:activerecord
: Это расширяет интерфейс «вложения» с поддержкой ActiveRecord. Всякий раз, когда включен модуль «вложения», в модель добавляются дополнительные обратные вызовы. -
:determine_mime_type
: хранит фактический тип MIME загруженного файла. -
:logging
: плагин logging регистрирует все выполненные сохранения / обработки / удаления.Rails.logger
вRails.logger
:logger
, мы изменим регистратор, чтобы он был полезен в нашем приложении Rails. -
:remove_attachment
: плагин remove_attachment позволяет удалять вложения с помощью флажков в веб-форме. -
:store_dimensions
: этот плагин извлекает и сохраняет размеры загруженного изображения. -
:validation_helpers
: предоставляет вспомогательные методы для проверки вложенных файлов. Взгляните на следующий блокAttacher
:
app / models / image uploader.rb_
Attacher.validate do validate_max_size 2.megabytes, message: 'is too large (max is 2 MB)' validate_mime_type_inclusion ['image/jpg', 'image/jpeg', 'image/png', 'image/gif'] end
Валидаторы довольно просты. Здесь мы ограничиваем размер до 2 МБ или меньше, а допустимые типы файлов — jpg
, png
и gif
.
-
:versions
: плагин версий позволяет вашему загрузчику иметь дело с версиями изображения. Для создания версий вы просто возвращаете хеш версий, как мы сделали в нашем загрузчике.
app / models / image uploader.rb_
plugin :versions, names: [:original, :thumb]
Теперь в методе process
обработайте любую версию (кроме :original
) и верните ее. Как вы можете видеть, мы делаем это для фазы store
, то есть, когда файл сохраняется:
... def process(io, context) case context[:phase] when :store thumb = resize_to_limit!(io.download, 200, 200) { original: io, thumb: thumb } end end
Настройте ваш BooksController
:
app / controllers / books controller.rb_
class BooksController < ApplicationController before_action :set_book, only: [:show, :edit, :update, :destroy] def index @books = Book.all end def show end def new @book = Book.new end def edit end def create @book = Book.new(book_params) if @book.save redirect_to @book, notice: 'book was successfully created.' else render :new end end def update if @book.update(book_params) redirect_to @book, notice: 'book was successfully updated.' else render :edit end end def destroy @book.destroy redirect_to books_url, notice: 'book was successfully destroyed.' end private def set_book @book = Book.find(params[:id]) end def book_params params.require(:book).permit(:name, :image, :remove_image) end end
Вам нужно создать инициализатор для Shrine:
touch config/initializers/shrine.rb
Вставьте в следующее:
конфиг / Инициализаторы / shrine.rb
require "shrine" require "shrine/storage/file_system" require "image_processing/mini_magick" Shrine.storages = { cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"), }
Shrine поставляется с двумя (2) хранилищами по умолчанию: FileSystem
и S3
.
Файловая система
Файловая система хранения занимается загрузками в файловую систему самого приложения. Он инициализируется с base folder
и prefix
, как у нас выше. prefix
является каталогом относительно base folder
которой будут храниться файлы, и включается в URL.
S3
Хранилище S3 отвечает за загрузку в сервис S3 Amazon Web Service (AWS). Чтобы использовать хранилище S3, вам понадобится гем aws-sdk в вашем Gemfile
. Не забудьте bundle install
.
Теперь перейдите к config/initilazers/shrine.rb
чтобы настроить хранилище S3.
конфиг / Инициализаторы / shrine.rb
require "shrine" require "shrine/storage/s3" s3_options = { access_key_id: ENV.fetch("S3_ACCESS_KEY_ID"), secret_access_key: ENV.fetch("S3_SECRET_ACCESS_KEY"), region: ENV.fetch("S3_REGION"), bucket: ENV.fetch("S3_BUCKET"), } Shrine.storages = { cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options), store: Shrine::Storage::S3.new(prefix: "store", **s3_options), }
Сначала нам требуется shrine
и shrine s3
. Параметру prefix
S3 передается имя сегмента, в который будут загружены наши файлы. В приведенном выше примере будет два (2) сегмента; один для store
, другой для cache
. Следующая опция, s3-options
, дает нам доступ к AWS. Вам нужно будет получить свой ключ доступа AWS и секретный ключ доступа и поместить их в переменные среды.
Теперь давайте работать над взглядами. Вид, чтобы держать нашу form
:
app / views / books / form.html.erb_
<%= form_for(@book) do |f| %> <%= render "error_messages", target: @book %> <div class="field"> <%= f.label :name %><br> <%= f.text_field :name %> </div> <div class="field"> <%= f.label :image %><br> <%= f.file_field :image %> </div> <%- if @book.image_data? %> <div class="field"> <%= image_tag @book.image_url(:thumb) %> </div> <div class="field"> Remove attachment: <%= f.check_box :remove_image %> </div> <%- end %> <div class="actions"> <%= f.submit %> </div> <% end %>
Я хочу, чтобы мы разделили сообщения об ошибках, это новая привычка, которую я недавно разработал. Создайте в ваших представлениях папку с именем application
. Создайте частичный файл с _error_messages.html.erb
j _error_messages.html.erb
и вставьте в сообщения об ошибках:
app / views / application / error messages.html.erb
<% if target.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@book.errors.count, "error") %> prohibited this book from being saved:</h2> <ul> <% @book.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %>
Форма действительно простая. Просто используйте form.file_field :image
для рендеринга соответствующего поля.
Давайте работать на странице show
. На этом этапе, когда мы пытаемся отправить форму, мы получаем сообщение « Action Controller: Exception caught
из-за отсутствия шаблона show
.
Вот так должен выглядеть код представления вашей страницы show
.
приложение / просмотров / книги / show.html.erb
<p id="notice"><%= notice %></p> <div> <strong>Name:</strong> <%= @book.name %> </div> <div> <strong>Image:</strong> <%= image_tag @book.image_url(:original) %> </div> <%= link_to 'Edit', edit_book_path(@book) %> | <%= link_to 'Back', books_path %>
Давайте настроим нашу страницу Edit
тоже:
приложение / просмотров / книги / html.erb
<h1>Editing Book</h1> <%= render 'form' %> <%= link_to 'Show', @book %> | <%= link_to 'Back', books_path %>
И index
страницы:
приложение / просмотров / книги / index.html.erb
<p id="notice"><%= notice %></p> <h1>Listing Books</h1> <table> <thead> <tr> <th>Name</th> <th>Image</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @books.each do |book| %> <tr> <td><%= book.name %></td> <td><%= image_tag book.image_url(:thumb) %></td> <td><%= link_to 'Show', book %></td> <td><%= link_to 'Edit', edit_book_path(book) %></td> <td><%= link_to 'Destroy', book, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </tbody> </table> <br> <%= link_to 'New Book', new_book_path %>
Версии
В вашем приложении Rails вы можете хранить миниатюры (или другие версии) вместе с исходным изображением. Для этого вам нужно загрузить плагин versions
, о котором я упоминал выше. Мы сделали это уже в файле image_uploader.rb
. Теперь просто #process
метод #process
возвращающий хэш версий:
app / views / models / image uploader.rb_
class ImageUploader < Shrine include ImageProcessing::MiniMagick plugin :activerecord plugin :determine_mime_type plugin :logging, logger: Rails.logger plugin :remove_attachment plugin :store_dimensions plugin :validation_helpers plugin :versions, names: [:original, :large, :medium, :small, :thumb] Attacher.validate do validate_max_size 2.megabytes, message: 'is too large (max is 2 MB)' validate_mime_type_inclusion ['image/jpg', 'image/jpeg', 'image/png', 'image/gif'] end def process(io, context) if context[:phase] == :store size_700 = resize_to_limit!(io.download, 700, 700) size_500 = resize_to_limit(size_700, 500, 500) size_300 = resize_to_limit(size_500, 300, 300) thumb = resize_to_limit(size_300, 200, 200) { original: io, large: size_700, medium: size_500, small: size_300, thumb: thumb } end end end
Вы можете выбрать, какую из версий вы хотите отобразить в представлении, как мы сделали с представлением для index
. Откройте index
представление и измените визуализированное изображение на уменьшенную версию.
приложение / просмотров / книги / index.html.erb
<p id="notice"><%= notice %></p> <h1>Listing Books</h1> <table> <thead> <tr> <th>Name</th> <th>Image</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @books.each do |book| %> <tr> <td><%= book.name %></td> <td><%= image_tag book.image_url(:small) %></td> <td><%= link_to 'Show', book %></td> <td><%= link_to 'Edit', edit_book_path(book) %></td> <td><%= link_to 'Destroy', book, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </tbody> </table> <br> <%= link_to 'New Book', new_book_path %>
Вывод
В этой статье мы узнали, как включить загрузку изображений в нашем приложении Rails с помощью Shrine. Вы можете проверить страницу Shrine на Github, если вы хотите попробовать некоторые интересные функции, не упомянутые здесь. У Shrine есть отличная поддержка для добавления файлов в фоновые задания или прямые загрузки, просто чтобы назвать несколько функций, которые мы не рассмотрели сегодня. Кроме того, Shrine не только для Rails, вы можете использовать его с любым приложением Ruby.
Спасибо за чтение и до скорой встречи!