Задача: показать как реализовать индикатор загрузки файла при различных конфигурациях Ruby on Rails:
Я разделю цикл статей на три части.
mongrel
mongrel(s)+nginx
mod_rails
Нижеописанное проверено с:
В основе легла статья, линк на которую есть в самом конце. Но повторив ее у меня на новых рельсах и многрелах ничего не получилось. Я немножно ее изменил.
Создание каркаса:
Добавление роутов:
Этап подготовки завершен. Теперь надо немного пояснить каким образом фунционирует индикатор загрузки файлов. Сабмит формы должен идти в iframe, а с самой страницы помощью ajax запросов можно с нужной частотой обращаться к определенному контроллеру, который будет возвращать текущее состояние (можно в json, можно html, здесь сделано javascript-ом), и в зависимости от них на странице двигать индикатор.
Итак, контроллер один, а в нем три метода: index для вывода формы для загрузки, upload — обработка загруженного файла, progress — для получения данных о статусе загрузки. Контроллер выглядит следующим образом:
skip_before_filter необходим так как запросы будет идти без токена. @upid — некий уникальный идентификатор.
Теперь необходимо написать некий плагин для монгрела, который будет перехватывать аплоад файлов (а точнее обращение к конкретному url) и хранить данные о состоянии загрузки, кроме того к этим данным нужно как-то обращаться. Хорошо, что это уже реализовано и код можно взять из svn:
Я предлагаю сделать следующим образом: создать файл lib/progress_plugin.rb со следующим содержимым:
После этого создать конфиг для монгрела (по сути обычный ruby-файл), который будет подключать этот плагин к монгрелу:
Поместить в mongrel_upload_progress.conf нужно следующее:
path_info это роут который будет перехватываться плагином для отслеживания состояния загрузки.
Форму загрузки и все нужные js-ники можно взять тут.
Осталось просто запустить монгрел с конфигурационным файлом:
Индикатор в браузере будет выглядить примерно так:
Если монгрелов несколько можно использовать drb, детали описаны в ссылке ниже.
Никаких косметических изменений в рамках этой статьи я не делал. В более красивом виде индикатор можно посмотреть на rghost.ru
Конечно жаль признать, что все это написано скорей всего зря, так как простым монгрелом сейчас мало кто обходится. Обычно перед монгрелом ставят nginx или вообще заменяют его apache-ем с mod_passenger. Статьи о них я планирую написать попозже.
mongrel upload progress
Я разделю цикл статей на три части.
mongrel
mongrel(s)+nginx
mod_rails
Нижеописанное проверено с:
Ruby on Rails 2.2.2
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]
mongrel 1.1.5
Mac OS X 10.5.6
Часть I. Как это сделать с mongrel'ом
В основе легла статья, линк на которую есть в самом конце. Но повторив ее у меня на новых рельсах и многрелах ничего не получилось. Я немножно ее изменил.
Создание каркаса:
rails upload
cd upload
rm public/index.html
script/generate controller home index upload progress
touch app/views/layouts/application.html.erb
Добавление роутов:
Copy Source | Copy HTML
- ActionController::Routing::Routes.draw do |map|
- map.root :controller => 'home'
- map.upload '/upload', :controller => 'home', :action => 'upload'
- map.progress '/progress', :controller => 'home', :action => 'progress'
- end
-
Этап подготовки завершен. Теперь надо немного пояснить каким образом фунционирует индикатор загрузки файлов. Сабмит формы должен идти в iframe, а с самой страницы помощью ajax запросов можно с нужной частотой обращаться к определенному контроллеру, который будет возвращать текущее состояние (можно в json, можно html, здесь сделано javascript-ом), и в зависимости от них на странице двигать индикатор.
Итак, контроллер один, а в нем три метода: index для вывода формы для загрузки, upload — обработка загруженного файла, progress — для получения данных о статусе загрузки. Контроллер выглядит следующим образом:
Copy Source | Copy HTML
- class HomeController < ApplicationController
- skip_before_filter :verify_authenticity_token, :only => [:progress]
-
- def index
- @upid = Time.now.tv_sec.to_s
- end
-
- def upload
- render :text => 'done'
- end
-
- def progress
- @status = Mongrel::Uploads.check(params[:upload_id])
- respond_to do |format|
- format.js
- end
- end
- end
skip_before_filter необходим так как запросы будет идти без токена. @upid — некий уникальный идентификатор.
Теперь необходимо написать некий плагин для монгрела, который будет перехватывать аплоад файлов (а точнее обращение к конкретному url) и хранить данные о состоянии загрузки, кроме того к этим данным нужно как-то обращаться. Хорошо, что это уже реализовано и код можно взять из svn:
svn co svn://rubyforge.org/var/svn/mongrel/trunk/projects/mongrel_upload_progress
Я предлагаю сделать следующим образом: создать файл lib/progress_plugin.rb со следующим содержимым:
Copy Source | Copy HTML
- class Upload < GemPlugin::Plugin "/handlers"
- include Mongrel::HttpHandlerPlugin
-
- def initialize(options = {})
- @path_info = Array(options[:path_info])
- @frequency = options[:frequency] || 3
- @request_notify = true
- if options[:drb]
- require 'drb'
- DRb.start_service
- Mongrel.const_set :Uploads, DRbObject.new(nil, options[:drb])
- else
- Mongrel.const_set :Uploads, Mongrel::UploadProgress.new
- end
- Mongrel::Uploads.debug = true if options[:debug]
- end
-
- def request_begins(params)
- upload_notify(:add, params, params[Mongrel::Const::CONTENT_LENGTH].to_i)
- end
-
- def request_progress(params, clen, total)
- upload_notify(:mark, params, clen)
- end
-
- def process(request, response)
- upload_notify(:finish, request.params)
- end
-
- private
- def upload_notify(action, params, *args)
- return unless @path_info.include?(params['PATH_INFO']) &&
- params[Mongrel::Const::REQUEST_METHOD] == 'POST' &&
- upload_id = Mongrel::HttpRequest.query_parse(params['QUERY_STRING'])['upload_id']
- if action == :mark
- last_checked_time = Mongrel::Uploads.last_checked(upload_id)
- return unless last_checked_time && Time.now - last_checked_time > @frequency
- end
- Mongrel::Uploads.send(action, upload_id, *args)
- Mongrel::Uploads.update_checked_time(upload_id) unless action == :finish
- end
- end
-
- # Keeps track of the status of all currently processing uploads
- class Mongrel::UploadProgress
- attr_accessor :debug
- def initialize
- @guard = Mutex.new
- @counters = {}
- end
-
- def check(upid)
- @counters[upid].last rescue nil
- end
-
- def last_checked(upid)
- @counters[upid].first rescue nil
- end
-
- def update_checked_time(upid)
- @guard.synchronize { @counters[upid][0] = Time.now }
- end
-
- def add(upid, size)
- @guard.synchronize do
- @counters[upid] = [Time.now, {:size => size, :received => 0}]
- puts "#{upid}: Added" if @debug
- end
- end
-
- def mark(upid, len)
- return unless status = check(upid)
- puts "#{upid}: Marking" if @debug
- @guard.synchronize { status[:received] = status[:size] - len }
- end
-
- def finish(upid)
- @guard.synchronize do
- puts "#{upid}: Finished" if @debug
- @counters.delete(upid)
- end
- end
-
- def list
- @counters.keys.sort
- end
- end
-
После этого создать конфиг для монгрела (по сути обычный ruby-файл), который будет подключать этот плагин к монгрелу:
touch config/mongrel_upload_progress.conf
Поместить в mongrel_upload_progress.conf нужно следующее:
Copy Source | Copy HTML
- require 'progress_plugin'
- uri "/", :handler => plugin('/handlers/upload', :path_info => '/upload'), :in_front => true
-
path_info это роут который будет перехватываться плагином для отслеживания состояния загрузки.
Форму загрузки и все нужные js-ники можно взять тут.
Осталось просто запустить монгрел с конфигурационным файлом:
mongrel_rails start -S config/mongrel_upload_progress.conf
Индикатор в браузере будет выглядить примерно так:
Если монгрелов несколько можно использовать drb, детали описаны в ссылке ниже.
Никаких косметических изменений в рамках этой статьи я не делал. В более красивом виде индикатор можно посмотреть на rghost.ru
Конечно жаль признать, что все это написано скорей всего зря, так как простым монгрелом сейчас мало кто обходится. Обычно перед монгрелом ставят nginx или вообще заменяют его apache-ем с mod_passenger. Статьи о них я планирую написать попозже.
Список использованной литературы
mongrel upload progress