Это последняя статья в серии «Загрузка с Rails». В последние пару месяцев мы уже обсуждали драгоценности Shrine, Dragonfly и Carrierwave. Сегодняшний гость — Paperclip от Thoughtbot, компании, которая управляет такими драгоценными камнями, как FactoryGirl и Bourbon.
Paperclip, вероятно, является самым популярным решением для управления вложениями для Rails (более 13 миллионов загрузок), и на то есть веская причина: в нем много функций, большое сообщество и подробная документация. Надеюсь, вы хотите узнать больше об этом драгоценном камне!
В этой статье вы узнаете, как:
- Подготовьтесь к установке скрепки
- Интеграция Paperclip в приложение Rails
- Добавить вложения проверки
- Генерация миниатюр и обработка изображений
- Запутывать URL
- Хранить вложения на Amazon S3
- Безопасные файлы в облаке с помощью логики авторизации
Исходный код этой статьи доступен на GitHub .
Препараты
Прежде чем мы углубимся в код, давайте сначала обсудим некоторые предостережения, о которых вам нужно знать, чтобы успешно работать с Paperclip:
- Последняя версия Paperclip поддерживает Rails 4.2+ и Ruby 2.1+. Этот драгоценный камень также можно использовать без Rails.
- ImageMagick должен быть установлен на вашем компьютере (он доступен для всех основных платформ), и Paperclip должен иметь к нему доступ.
- Команда
file
должна быть доступна из командной строки. Для Windows он доступен через Development Kit, поэтому следуйте этим инструкциям, если у вас еще не установлен DevKit.
Когда вы будете готовы, продолжайте и создайте новое приложение Rails (я буду использовать Rails 5.0.2) без набора тестов по умолчанию:
1
|
rails new UploadingWithPaperclip -T
|
Интеграция скрепки
Вставьте в скрепку драгоценный камень:
Gemfile
1
|
gem «paperclip», «~> 5.1»
|
Установите это:
1
|
bundle install
|
Предположим, мы создаем приложение для книжных полок, которое представляет список книг. Каждая книга будет иметь название, описание, имя автора и изображение обложки. Для начала создайте и примените следующую миграцию:
1
2
|
rails g model Book title:string description:text image:attachment author:string
rails db:migrate
|
Обратите внимание на тип attachment
который представлен для нас Paperclip. Под капотом он собирается создать для нас четыре поля:
-
image_file_name
-
image_file_size
-
image_content_type
-
image_updated_at
В отличие от драгоценных камней Shrine и Carrierwave, Paperclip не имеет отдельного файла с конфигурациями. Все настройки определяются внутри самой модели с has_attached_file
метода has_attached_file
, поэтому добавьте его сейчас:
модели / book.rb
1
|
has_attached_file :image
|
Прежде чем перейти к основной части, давайте также создадим контроллер вместе с некоторыми видами и маршрутами.
Создание контроллера, представлений и маршрутов
Наш контроллер будет очень простым:
books_controller.rb
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
class BooksController < ApplicationController
before_action :set_book, only: [:show, :download]
def index
@books = Book.order(‘created_at DESC’)
end
def new
@book = Book.new
end
def show
end
def create
@book = Book.new(book_params)
if @book.save
redirect_to books_path
else
render :new
end
end
private
def book_params
params.require(:book).permit(:title, :description, :image, :author)
end
def set_book
@book = Book.find(params[:id])
end
end
|
Вот индексное представление и частичное:
просмотров / книги / index.html.erb
1
2
3
4
5
6
|
<h1>Bookshelf</h1>
<%= link_to ‘Add book’, new_book_path %>
<ul>
<%= render @books %>
</ul>
|
просмотров / книги / _book.html.erb
1
2
3
|
<li>
<strong><%= link_to book.title, book_path(book) %></strong> by <%= book.author %>
</li>
|
Теперь маршруты:
конфиг / routes.rb
1
2
3
4
|
Rails.application.routes.draw do
resources :books
root to: ‘books#index’
end
|
Ницца! Теперь перейдем к основному разделу и закодируем новое действие и форму.
Загрузка файлов
В общем, загружать с помощью Paperclip легко. Вам нужно только разрешить соответствующий атрибут (в нашем случае это атрибут image
, и мы уже разрешили его) и представить поле формы в вашей форме. Давай сделаем это сейчас:
просмотров / книги / new.html.erb
1
2
3
|
<h1>Add book</h1>
<%= render ‘form’, book: @book %>
|
просмотров / книги / _form.html.erb
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<%= form_for book do |f|
<div>
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div>
<%= f.label :author %>
<%= f.text_field :author %>
</div>
<div>
<%= f.label :description %>
<%= f.text_area :description %>
</div>
<div>
<%= f.label :image %>
<%= f.file_field :image %>
</div>
<%= f.submit %>
<% end %>
|
С этой настройкой вы уже можете начинать выполнять загрузку, но также неплохо бы ввести некоторые проверки.
Добавление проверок
Валидации в Paperclip могут быть написаны с использованием старых помощников, таких как validates_attachment_presence
и validates_attachment_content_type
или с помощью метода validates_attachment
для определения нескольких правил одновременно. Давайте придерживаться последнего варианта:
модели / book.rb
1
2
3
|
validates_attachment :image,
content_type: { content_type: /\Aimage\/.*\z/ },
size: { less_than: 1.megabyte }
|
Код действительно прост, как вы можете видеть. Мы требуем, чтобы файл был размером менее 1 мегабайта. Обратите внимание, что если проверка не пройдена, никакая постобработка не будет выполнена. В Paperclip уже есть некоторые сообщения об ошибках, заданные для английского языка, но если вы хотите поддерживать другие языки, включите гем paperclip-i18n в свой Gemfile .
Еще одна важная вещь, которую стоит упомянуть, это то, что Paperclip требует от вас проверить тип содержимого или имя файла всех вложений, в противном случае это вызовет ошибку. Если вы на 100% уверены, что вам не нужны такие проверки (что является редким случаем), используйте do_not_validate_attachment_file_type
чтобы явно do_not_validate_attachment_file_type
, какие поля проверять не следует.
Добавив проверки, давайте также отобразим сообщения об ошибках в нашей форме:
просмотров / общий / _errors.html.erb
1
2
3
4
5
6
7
8
|
<% if object.errors.any?
<h3>Some errors were found:</h3>
<ul>
<% object.errors.full_messages.each do |message|
<li><%= message %></li>
<% end %>
</ul>
<% end %>
|
просмотров / книги / _form.html.erb
1
|
<%= render ‘shared/errors’, object: book %>
|
Отображение изображений
Итак, теперь загруженные изображения должны отображаться как-то. Это делается с помощью помощника image_tag
и метода url
. Создать представление шоу :
просмотров / книги / show.html.erb
1
2
3
4
5
|
<h1><%= @book.title %> by <%= @book.author %></h1>
<%= image_tag(@book.image.url) if @book.image.exists?
<p><%= @book.description %></p>
|
Мы отображаем изображение, только если оно действительно существует на диске. Более того, если вы используете облачное хранилище, то Paperclip выполнит сетевой запрос и проверит наличие файла. Конечно, эта операция может занять некоторое время, так что вы могли бы использовать present?
или file?
вместо этого: они просто image_file_name
заполнено ли поле image_file_name
каким-либо содержимым.
URI Запутывание
По умолчанию все вложения хранятся в общедоступной / системной папке, поэтому вы, вероятно, захотите исключить ее из системы контроля версий:
.gitignore
1
|
public/system
|
Тем не менее, отображение полного URI для файла не всегда может быть хорошей идеей, и вам может потребоваться каким-то образом его скрыть. Самый простой способ включить запутывание — предоставить два параметра has_attached_file method
:
модели / book.rb
1
2
|
url: «/system/:hash.:extension»,
hash_secret: «longSecretString»
|
Надлежащие значения будут автоматически вставлены в url
. hash_secret
является обязательным полем, и его проще всего создать с помощью:
1
|
rails secret
|
Работа со стилями
Во многих случаях предпочтительно отображать миниатюру изображения с некоторой предопределенной шириной и высотой, чтобы сохранить пропускную способность. Скрепка решает эту проблему с помощью стилей : у каждого стиля есть имя и набор правил, таких как размеры, формат, качество и т. Д.
Предположим, что мы хотим, чтобы исходное изображение и его эскиз были преобразованы в формат JPEG. Миниатюра должна быть обрезана до 300x300px:
модели / book.rb
1
2
3
4
5
|
has_attached_file :image,
styles: {
thumb: [«300×300#», :jpeg],
original: [:jpeg]
}
|
#
— настройка геометрии, означающая: «Обрезать при необходимости, сохраняя соотношение сторон».
Мы также можем предоставить дополнительные параметры преобразования для каждого стиля. Например, давайте обеспечим 70% качества больших пальцев при удалении всех метаданных и 90% качества исходного изображения, чтобы сделать его немного меньше:
модели / book.rb
1
2
3
4
5
6
7
8
9
|
has_attached_file :image,
styles: {
thumb: [«300×300#», :jpeg],
original: [:jpeg]
},
convert_options: {
thumb: «-quality 70 -strip»,
original: «-quality 90»
}
|
Ницца! Отобразите эскиз и укажите ссылку на исходное изображение:
просмотров / книги / show.html.erb
1
|
<%= link_to(image_tag(@book.image.url(:thumb)), @book.image.url, target: ‘_blank’) if @book.image.exists?
|
Обратите внимание, что в отличие от Carrierwave, например, Paperclip не позволяет писать @book.image.thumb.url
.
Если по какой-либо причине вы хотите обновить загруженные изображения вручную, вы можете использовать следующие команды для обновления только миниатюр, добавления отсутствующих стилей или обновления всех изображений:
-
rake paperclip:refresh:thumbnails CLASS=Book
-
rake paperclip:refresh:missing_styles CLASS=Book
-
rake paperclip:refresh CLASS=Book
Хранение файлов в облаке
Как и все аналогичные решения, Paperclip позволяет загружать файлы в облако. Он имеет встроенную поддержку адаптеров S3 и Fog, но есть также сторонние гемы для Azure и Dropbox. В этом разделе я покажу вам, как интегрировать Paperclip с Amazon S3. Во-первых, добавьте гем aws-sdk :
1
|
gem ‘aws-sdk’
|
Установите это:
1
|
bundle install
|
Затем предоставьте новый набор параметров для метода has_attached_file
:
модели / book.rb
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
has_attached_file :image,
styles: {
thumb: [«300×300#», :jpeg],
original: [:jpeg]
},
convert_options: {
thumb: «-quality 70 -strip»,
original: «-quality 90»
},
storage: :s3,
s3_credentials: {
access_key_id: ENV[«S3_KEY»],
secret_access_key: ENV[«S3_SECRET»],
bucket: ENV[«S3_BUCKET»]
},
s3_region: ENV[«S3_REGION»]
|
Здесь я придерживаюсь драгоценного камня dotenv-rails для установки переменных окружения. Вы можете предоставить все значения непосредственно внутри модели, но не делать ее общедоступной.
Что интересно, s3_credentials
также принимает путь к файлу YAML, содержащему ваши ключи и имя s3_credentials
. Кроме того, вы можете установить разные значения для разных сред, например:
1
2
3
4
5
6
|
development:
access_key_id: key1
secret_access_key: secret1
production:
access_key_id: key2
secret_access_key: secret2
|
Это оно! Все загружаемые вами файлы теперь будут находиться в вашей корзине S3.
Защита файлов в облаке
Предположим, вы не хотите, чтобы ваши загруженные файлы были доступны всем. По умолчанию все загрузки в облако помечаются как общедоступные, что означает, что любой может открыть файл по прямой ссылке. Если вы хотите ввести некоторую логику авторизации и проверить, кто может просматривать файл, установите для параметра s3_permissions
значение :private
например так:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
has_attached_file :image,
styles: {
thumb: [«300×300#», :jpeg],
original: [:jpeg]
},
convert_options: {
thumb: «-quality 70 -strip»,
original: «-quality 90»
},
storage: :s3,
s3_credentials: {
access_key_id: ENV[«S3_KEY»],
secret_access_key: ENV[«S3_SECRET»],
bucket: ENV[«S3_BUCKET»]
},
s3_region: ENV[«S3_REGION»],
s3_permissions: :private
|
Однако теперь никто кроме вас не сможет увидеть файлы. Поэтому давайте создадим новое действие download
для BooksController
:
books_controller.rb
1
2
3
|
def download
redirect_to @book.image.expiring_url
end
|
Это действие просто перенаправит пользователей на изображение по истекающей ссылке. Используя этот подход, вы можете теперь представить любую логику авторизации, используя такие гемы, как CanCanCan или Pundit .
Не забудьте установить маршрут участника:
конфиг / routes.rb
1
2
3
4
5
|
resources :books do
member do
get ‘download’
end
end
|
Помощник должен использоваться следующим образом:
1
|
link_to(‘View image’, download_book_path(@book), target: ‘_blank’)
|
Вывод
Мы подошли к концу этой статьи! Сегодня мы увидели Paperclip, решение для управления вложениями для Rails, и обсудили его основные концепции. У этого драгоценного камня гораздо больше, поэтому обязательно ознакомьтесь с его документацией .
Кроме того, я рекомендую посетить вики-страницу Paperclip, поскольку на ней представлен список учебных пособий «как делать» и несколько ссылок на сторонние гемы, поддерживающие Azure и Cloudinary, и позволяющие легко минимизировать загружаемые файлы.
Спасибо, что остаетесь со мной, и до скорой встречи!