Статьи

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

В моей предыдущей статье мы узнали, как включить загрузку файлов в 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.

Спасибо за чтение и до скорой встречи!