Ускорьте загрузку S3 с помощью скрепки - PullRequest
5 голосов
/ 29 января 2010

Я использую скрепку для загрузки изображений в S3. Но я заметил, что эта загрузка очень медленная. Я думаю, потому что до завершения отправки файл должен пройти мимо моего сервера, быть обработан и отправлен на сервер S3.

Есть ли способ ускорить это?

спасибо

Ответы [ 6 ]

1 голос
/ 26 января 2017

Вы не опубликовали никакого кода, поэтому я собираюсь сделать несколько предположений здесь:

  • в вашем проекте у вас есть Album и Image модель
  • Album has_many :images
  • У вас уже есть скрепка и aws-sdk , правильно настроенная с ведрами и всем остальным
  • Вы загружаете много изображений наодин раз

Чтобы загрузить много изображений, ваша форма будет выглядеть примерно так:

<%= form_for @album, html: { multipart: true } do |f| %>
  <%= f.file_field :files, accept: 'image/png,image/jpeg,image/gif', multiple: true %>

  <%= f.submit %>
<% end %>

Ваш контроллер будет выглядеть примерно так

class AlbumsController < ApplicationController
  def update
    @album = Album.find params[:id]
    @album.update album_params
    redirect_to @album, notice: 'Images saved'
  end

  def album_params
    params.require(:album).permit files: []
  end
end

Чтобы манипулировать изображениями с помощью альбома, вам понадобится

class Album < ApplicationRecord
  has_many :images, dependent: :destroy

  accepts_nested_attributes_for :images, allow_destroy: true

  def files=(array = [])
    array.each do |f|
      images.create file: f
    end
  end
end

Ваш файл Image будет выглядеть следующим образом

class Image < ApplicationRecord
  belongs_to :album

  has_attached_file :file, styles: { thumbnail: '500x500#' }, default_url: '/default.jpg'

  validates_attachment_content_type :file, content_type: /\Aimage\/.*\Z/
end

Это просто важный материал.При такой настройке загрузка 22 изображений общим объемом 12 МБ занимает :files= метод 41.1806895 секунд для выполнения в среднем на моем локальном сервере.Чтобы проверить, сколько времени занимает запуск метода, используйте:

def files=(array = [])
  start = Time.now

  array.each do |f|
    images.create file: f
  end

  p "ELAPSED TIME: #{Time.now - start}"
end

Вы запрашиваете более быструю загрузку множества изображений.Есть несколько способов сделать это.Использование заданий не будет работать, поскольку вы не можете передавать сложные данные, например изображения, в задание.


Используйте взамен delayed_paperclip .Он перемещает создание стилей изображения (например, thumbnail: '500x500#') в фоновые задания.

Gemfile

source 'https://rubygems.org'

ruby '2.3.0'

...
gem 'delayed_paperclip'
...

Файл изображения

class Image < ApplicationRecord
  ...
  process_in_background :file
end

Ускоряет метод :files=,Та же самая загрузка, что и раньше (22 изображения, 12 МБ) с этой настройкой, заняла 23.13998 секунд на моей машине.Это в 1.77963 раз быстрее, чем раньше.


Еще один способ ускорить процесс - использовать Threads .Удалите delayed_paperclip из Gemfile и строки process_in_background :file.Обновите свой :files= метод:

def files=(array = [])    
  threads = []

  array.each do |f|
    threads << Thread.new do
      images.create file: f
    end
  end

  threads.each(&:join)
end

Вы можете попробовать это, но получите странную ошибку и увидите только 4 сохраненных изображения.Вы также должны использовать Mutex .Кроме того, вы не должны использовать :join в потоках, потому что, если вы присоединитесь, метод будет ждать завершения потоков.

def files=(array = [])
  semaphore = Mutex.new

  array.each do |f|
    Thread.new do
      semaphore.synchronize do
        images.create file: f
      end
    end
  end
end

С этим простым изменением метода и без добавления драгоценных камней,та же загрузка, что и раньше, запускается за 0.017628 секунд .Это в 1079 * 1,313 раз быстрее, чем delayed_paperclip.Это также * в 1082 * 2,336 раз быстрее, чем обычные настройки.


Что произойдет, если вы используете delayed_paperclip AND Threads?

Не меняйте метод :files=.Просто включите delayed_paperclip в свой Gemfile и добавьте обратно строку process_in_background :file.

При такой настройке на моей машине метод работает в среднем 0.001277 секунд.Это

  • 13,8 раз быстрее, чем Threads
  • 18 120,6 раз быстрее, чем delayed_paperclip
  • 32 248,0 раз быстрее, чем обычные настройки

Помните, это на моей машине, и я не проверял это на производстве.Я также на Wi-Fi, а не Ethernet.Все это может изменить результаты, но я думаю, что цифры говорят сами за себя.

Загрузка изображений быстрее.Готово.


ОБНОВЛЕНИЕ: Не использовать delayed_paperclip.Это может привести к загруженности базы данных, и некоторые изображения могут не сохраниться.Я проверял это.Я думаю, что просто использование потоков достаточно быстро.Удалите строку process_in_background из файла Image.Кроме того, вот как выглядит мой метод files=:

def files=(array = [])
  Thread.new do
    begin
      array.each { |f| images.create file: f }
    ensure
      ActiveRecord::Base.connection_pool.release_connection
    end
  end
end

Примечание: Поскольку мы сохраняем изображение, сохраняя его в фоновом режиме, а затем перенаправляем.На загружаемой странице еще не будет изображений.Пользователь должен обновить , чтобы обновить страницу.Одним из способов решения этой проблемы является использование опроса .Опрос - это когда JavaScript проверяет наличие каких-либо изменений каждые 5 секунд или около того и вносит изменения, если таковые имеются, на страницу.

Другой вариант заключается в использовании Web Sockets .Теперь, когда у нас есть Rails 5, мы можем использовать ActionCable .Каждый раз, когда изображение создается, мы транслируем обновление для альбома.Если пользователь находится на этой странице для этого альбома, он увидит, что обновления произойдут, как только они произойдут в базе данных, без обновления пользователя или когда браузер отправляет запрос каждые 5 секунд в бесконечном цикле.

Прикольные вещи.

1 голос
/ 29 января 2010

Как насчет загрузки прямо на S3?

Не уверен, что скрепка сделает это из коробки, но вы можете сделать это.

http://docs.amazonwebservices.com/AmazonS3/2006-03-01/dev/index.html?UsingHTTPPOST.html

1 голос
/ 29 января 2010

Хотите улучшить внешний вид загрузки или сделать ее более быстрой?

Если это первое, вы можете поместить свою логику обработки изображений в фоновую задачу, используя что-то вроде delayed_job . Таким образом, когда пользователь нажимает кнопку, он сразу же переходит на следующую страницу, пока вы обрабатываете изображение (вы можете показать заполнитель изображения «в процессе», пока задача не будет завершена).

Если это последнее, то это полностью зависит от вашего сервера и подключения к интернету. Где вы хостинг?

0 голосов
/ 27 мая 2011

Если вы в конечном итоге идете по пути загрузки непосредственно на S3, который выгружает работу с вашего сервера Rails, пожалуйста, ознакомьтесь с моими примерами проектов:

Пример проекта с использованием Rails 3, Flash и FancyUploader на основе MooTools для загрузки непосредственно в S3: https://github.com/iwasrobbed/Rails3-S3-Uploader-FancyUploader

Пример проекта с использованием Rails 3, Flash / Silverlight / GoogleGears / BrowserPlus и Pluquload на основе jQuery для загрузки непосредственно в S3: https://github.com/iwasrobbed/Rails3-S3-Uploader-Plupload

Кстати, вы можете выполнить постобработку с помощью Paperclip, используя что-то вроде этого в блоге, описанном:

http://www.railstoolkit.com/posts/fancyupload-amazon-s3-uploader-with-paperclip

0 голосов
/ 30 января 2010

Используйте отложенные задания, это хороший пример здесь
Или вы можете использовать флэш-загрузку.

0 голосов
/ 29 января 2010

Как рекомендует cwninja, мы загружаем файлы прямо на s3, чтобы избавиться от этой дополнительной загрузки. Мы используем модифицированную версию плагина, описанного в этом посте:

http://elctech.wpengine.com/2009/02/updates-on-rails-s3-flash-upload-plugin/

Наш изменен для обработки нескольких загрузок файлов (переписал объект flex

Не уверен, насколько хорошо это работает со скрепкой, мы используем attachment_fu, но было неплохо заставить его работать с этим.

...