- PVSM.RU - https://www.pvsm.ru -
Это вторая часть из серии постов о Shrine [1]. Цель этой серии статей – показать преимущества Shrine над существующими загрузчиками файлов.
class MyStorage
def upload(io, id, **options)
# выгружает `io` в указанный `id`
end
def url(id)
# возвращает URL-адрес файла с `id`
end
def open(id)
# возвращает файл по адресу `id` в качестве объекта типа IO
end
def exists?(id)
# возвращает, существование файла в хранилище
end
def delete(id)
# удаляет соответствующий файл из хранилища
end
end
Хранилища Shrine конфигурируются напрямую, передавая опции в конструктор (позаимствовано у Refile) и должны быть зарегистрированы в Shrine.storages
:
Shrine.storages[:s3] = Shrine::Storage::S3.new(
access_key_id: "abc",
secret_access_key: "xyz",
region: "eu-west-1",
bucket: "my-bucket",
)
В настоящее время для Shrine имеются поддержка файловой системы [3], S3 [4], Fog [5], Flickr [6], Cloudary [7], Transloadit [8], Uploadcare [9], Imgix [10], GridFS [11] и SQL [12], так что выбирайте.
Вы также можете легко написать свое хранилище, для этого есть руководство [13] и линтер [14], который автоматически проверит, правильно ли все работает.
class ImageUploader < Shrine
# image uploading logic goes here
end
Объекты Uploader выступают в качестве оберток вокруг хранилища, в них выполняется вся логика для загрузки, которая является общей для любого хранилища:
Создание экземпляра загрузчика с установленным параметром хранилища:
Shrine.storages[:disk] = Shrine::Storage::FileSystem.new(...)
uploader = ImageUploader.new(:disk)
uploader.upload(image) #=> #<Shrine::UploadedFile>
Загрузчики не знают о моделях; Они только работают с файлом, который будет загружен на вход, и возвращают представление загруженного файла на выходе. Поскольку это предполагает, что загрузчики являются stateless, это делает их поведение очень предсказуемым.
#upload
возвращает объект Shrine::UploadedFile
. Этот объект является полным представлением файла, который был загружен в хранилище.
uploaded_file = uploader.upload(image) #=> #<Shrine::UploadedFile>
uploaded_file.id #=> "43ksd9gkafg0dsl.jpg"
uploaded_file.storage #=> #<Shrine::Storage::FileSystem>
uploaded_file.metadata #=> {...}
Так как этот объект знает, куда он был загружен, он может предоставить много полезных методов:
uploaded_file.url # generates the URL
uploaded_file.download # downloads the file to the disk
uploaded_file.exists? # asks the storage if file exists
uploaded_file.open { |io| ... } # opens the file for reading
uploaded_file.delete # deletes the file from the storage
Этот объект определяется только хешем. Поскольку на хранилище можно ссылаться по его установленному параметру, этот хэш теперь может быть сериализован в JSON и сохранен в столбце базы данных.
uploaded_file.data #=>
# {
# "id" => "df9fk48saflg.jpg",
# "storage" => "disk",
# "metadata" => {...}
# }
uploaded_file.to_json #=> '{"id":"df9fk48saflg.jpg","storage":"disk","metadata":{...}}'
Объекты Shrine::UploadedFile
не зависят от загрузчиков. Это значительное отличие от CarrierWave и Paperclip, которые имеют такую зависимость с классами CarrierWave::Uploader::Base
и Paperclip::Attachment
.
IO
, который отвечает на #read
, #size
, #rewind
, #eof?
И #close
(как у Refile). Определяя этот строгий интерфейс, каждая функция Shrine теперь знает, что они могут полагаться только на эти методы, а это значит, что они будут работать правильно независимо от того, загружаете ли вы типы File
, StringIO
, ActionDispatch::Http::UploadedFile
, Rack
или удаленные файлы, которые загружаются стримом [15].
Кроме того, Shrine::UploadedFile
сам по себе является объектом типа IO
, обертывая любой загруженный файл под тем же унифицированным интерфейсом. Это делает перемещение файла с одного хранилища на другое действительно удобным. Кроме того, это позволяет оптимизировать некоторые закачки путем пропуска процесса скачивания и повторной загрузки, например, использовать копию S3, если оба файла из S3, или просто отправить запрос URL, если хранилище поддерживает его.
cache = ImageUploader.new(:s3_temporary)
cached_file = cache.upload(image)
store = ImageUploader.new(:s3_permanent)
store.upload(cached_file) #=> performs an S3 COPY request
# Loads the processing feature from "shrine/plugins/logging.rb"
Shrine.plugin :logging, logger: Rails.logger
Shrine поставляется с более чем 35 плагинами, и вы легко можете написать свои собственные. Плагиновая система Shrine — это адаптация Roda [18], о которой я писал [19] в прошлом.
Кроме того, загрузчики Shrine могут наследоваться (в отличие от CarrierWave [20]).
Shrine.plugin :logging # enables logging for all uploaders
class ImageUploader < Shrine
plugin :backup # stores backups only for this uploader (and its descendants)
end
Shrine, с другой стороны, имеет только одну обязательную но легкую зависимость — Down [25]. Down — это net/http обертка для загрузки файлов, которая улучшает open-uri [26] и поддерживает стриминг, и используется почти всеми хранилищами Shrine.
Кроме того, Shrine в целом загружается очень быстро, потому что вы загружаете код только для функционала, который вы используете. Для других загрузчиков требуется загрузить код для всего функционала, которые может вам не понадобиться. К примеру, Shrine загружается в 35 раз быстрее CarrierWave без загруженных плагинов и в 7 раз быстрее со всеми загруженными (исходник [27]) плагинами.
Каждый высокоуровневый интерфейс должен иметь хороший фундамент. Таким образом, c каким бы уровнем абстракции вы не работали, вы всегда сможете понять, что происходит. Основа Shrine состоит из классов Storage
, Shrine
и Shrine::UploadedFile
, каждый из которых имеет четко определенный интерфейс и обязанность.
Cтатьи из оригинальной серии в блоге автора библиотеки:
Автор: amerov
Источник [32]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ruby/255451
Ссылки в тексте:
[1] Shrine: https://github.com/janko-m/shrine
[2] предыдущем посте: https://habrahabr.ru/post/328558
[3] файловой системы: https://github.com/janko-m/shrine/blob/master/lib/shrine/storage/file_system.rb
[4] S3: https://github.com/janko-m/shrine/blob/master/lib/shrine/storage/s3.rb
[5] Fog: https://github.com/janko-m/shrine-fog
[6] Flickr: https://github.com/janko-m/shrine-flickr
[7] Cloudary: https://github.com/janko-m/shrine-cloudinary
[8] Transloadit: https://github.com/janko-m/shrine-transloadit
[9] Uploadcare: https://github.com/janko-m/shrine-uploadcare
[10] Imgix: https://github.com/janko-m/shrine-imgix
[11] GridFS: https://github.com/janko-m/shrine-gridfs
[12] SQL: https://github.com/janko-m/shrine-sql
[13] руководство: http://shrinerb.com/rdoc/files/doc/creating_storages_md.html
[14] линтер: https://github.com/janko-m/shrine/blob/master/lib/shrine/storage/linter.rb
[15] загружаются стримом: https://github.com/janko-m/down#streaming
[16] ядром: https://github.com/janko-m/shrine/blob/master/lib/shrine.rb
[17] плагины: http://shrinerb.com/#plugins
[18] Roda: https://github.com/jeremyevans/roda
[19] писал: https://twin.github.io/the-plugin-system-of-sequel-and-roda/
[20] в отличие от CarrierWave: https://jbhannah.net/articles/carrierwave-concerns/
[21] без библиотек: https://github.com/janko-m/shrine/blob/master/lib/shrine/plugins/validation_helpers.rb
[22] Open3: http://ruby-doc.org/stdlib-2.3.0/libdoc/open3/rdoc/Open3.html
[23] имеет проблемы: https://github.com/thoughtbot/paperclip/issues?utf8=%E2%9C%93&q=label%3A%22Spoof%20related%20or%20Mime%20types%22%20
[24] file: http://linux.die.net/man/1/file
[25] Down: https://github.com/janko-m/down
[26] улучшает open-uri: https://twin.github.io/improving-open-uri
[27] исходник: https://gist.github.com/janko-m/0d4269b9c7195b5e65cc947acf1cc028
[28] https://twin.github.io/better-file-uploads-with-shrine-uploader/: https://twin.github.io/better-file-uploads-with-shrine-uploader/
[29] Better File Uploads with Shrine: Attachment: https://twin.github.io/better-file-uploads-with-shrine-attachment
[30] Better File Uploads with Shrine: Processing: https://twin.github.io/better-file-uploads-with-shrine-processing
[31] Better File Uploads with Shrine: Metadata: https://twin.github.io/better-file-uploads-with-shrine-metadata
[32] Источник: https://habrahabr.ru/post/328788/
Нажмите здесь для печати.