Module: ActionController::UploadProgress
- Defined in:
- lib/action_controller/upload_progress.rb
Overview
THIS IS AN EXPERIMENTAL FEATURE
Which means that it doesn’t yet work on all systems. We’re still working on full compatibility. It’s thus not advised to use this unless you’ve verified it to work fully on all the systems that is a part of your environment. Consider this an extended preview.
To enable this module, add ActionController::Base.enable_upload_progress
to your config/environment.rb file.
Action Pack Upload Progress for multipart uploads
The UploadProgress module aids in the process of viewing an Ajax driven upload status when working with multipart forms. It offers a macro that will prepare an action for handling the cleanup of the Ajax updating including passing the redirect URL and custom parameters to the Javascript finish handler.
UploadProgress is available for all multipart uploads when the upload_status_for
macro is called in one of your controllers.
The progress is stored as an UploadProgress::Progress object in the session and is accessible in the controller and view with the upload_progress
method.
For help rendering the UploadProgress enabled form and supported elements, see ActionView::Helpers::UploadProgressHelper.
Automatic updating on upload actions
class DocumentController < ApplicationController
upload_status_for :create
def create
# ... Your document creation action
end
end
The upload_status_for
macro will override the rendering of the action passed if upload_id
is found in the query string. This allows for default behavior if Javascript is disabled. If you are tracking the upload progress then create
will now return the cleanup scripts that will terminate the polling of the upload status.
Customized status rendering
class DocumentController < ApplicationController
upload_status_for :create, :status => :custom_status
def create
# ... Your document creation action
end
def custom_status
# ... Override this action to return content to be replaced in
# the status container
render :inline => "<%= upload_progress.completed_percent rescue 0 %> % complete", :layout => false
end
The default status action is upload_status
. The results of this action are added used to replace the contents of the HTML elements defined in upload_status_tag
. Within upload_status
, you can load the Progress object from the session with the upload_progress
method and display your own results.
Completion of the upload status updating occurs automatically with an after_filter
call to finish_upload_status
. Because the upload must be posted into a hidden IFRAME to enable Ajax updates during the upload, finish_upload_status
overwrites the results of any previous render
or redirect_to
so it can render the necessary Javascript that will properly terminate the status updating loop, trigger the completion callback or redirect to the appropriate URL.
Basic Example (View):
<%= form_tag_with_upload_progress({:action => 'create'}, {:finish => 'alert("Document Uploaded")'}) %>
<%= upload_status_tag %>
<%= file_field 'document', 'file' %>
<%= end_form_tag %>
Basic Example (Controller):
class DocumentController < ApplicationController
upload_status_for :create
def create
@document = Document.create(params[:document])
end
end
Extended Example (View):
<%= form_tag_with_upload_progress({:action => 'create'}, {}, {:action => :custom_status}) %>
<%= upload_status_tag %>
<%= file_field 'document', 'file' %>
<%= submit_tag "Upload" %>
<%= end_form_tag %>
<%= form_tag_with_upload_progress({:action => 'add_preview'}, {:finish => 'alert(arguments[0])'}, {:action => :custom_status}) %>
<%= upload_status_tag %>
<%= submit_tag "Upload" %>
<%= file_field 'preview', 'file' %>
<%= end_form_tag %>
Extended Example (Controller):
class DocumentController < ApplicationController
upload_status_for :add_preview, :create, {:status => :custom_status}
def add_preview
@document = Document.find(params[:id])
@document.preview = Preview.create(params[:preview])
if @document.save
finish_upload_status "'Preview added'"
else
finish_upload_status "'Preview not added'"
end
end
def create
@document = Document.new(params[:document])
upload_progress.message = "Processing document..."
session.update
@document.save
redirect_to :action => 'show', :id => @document.id
end
def custom_status
render :inline => '<%= upload_progress_status %> <div>Updated at <%= Time.now %></div>', :layout => false
end
Environment checklist
This is an experimental feature that requires a specific webserver environment. Use the following checklist to confirm that you have an environment that supports upload progress.
Ruby:
-
Running the command ‘ruby -v` should print “ruby 1.8.2 (2004-12-25)” or older
Web server:
-
Apache 1.3, Apache 2.0 or Lighttpd 1.4 (need to build lighttpd from CVS)
FastCGI bindings:
-
> 0.8.6 and must be the compiled C version of the bindings
-
The command ‘ruby -e “p require(’fcgi.so’)”‘ should print “true”
Apache/Lighttpd FastCGI directives:
-
You must allow more than one FCGI server process to allow concurrent requests.
-
If there is only a single FCGI process you will not get the upload status updates.
-
You can check this by taking a look for running FCGI servers in your process list during a progress upload.
-
Apache directive: FastCGIConfig -minProcesses 2
-
Lighttpd directives taken from config/lighttpd.conf (min-procs):
fastcgi.server = ( ".fcgi" => ( "APP_NAME" => ( "socket" => "/tmp/APP_NAME1.socket", "bin-path" => "RAILS_ROOT/public/dispatch.fcgi", "min-procs" => 2 ) ) )
config/environment.rb:
-
Add the following line to your config/environment.rb and restart your web server.
-
ActionController::Base.enable_upload_progress
Development log:
-
When the upload progress is enabled by you will find something the following lines:
-
“Multipart upload with progress (id: 1, size: 85464)”
-
“Finished processing multipart upload in 0.363729s”
-
If you are properly running multiple FCGI processes, then you will see multiple entries for rendering the “upload_status” action before the “Finish processing…” log entry. This is a *good thing* :)
Defined Under Namespace
Modules: ClassMethods Classes: Progress
Class Method Summary collapse
-
.append_features(base) ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#current_upload_id ⇒ Object
Returns the
upload_id
from the query parameters or if it cannot be found in the query parameters, then return thelast_upload_id
. -
#finish_upload_status(client_js_argument = '') ⇒ Object
Overwrites the body rendered if the upload comes from a form that tracks the progress of the upload.
-
#last_upload_id ⇒ Object
Either returns the last saved
upload_id
or looks in the session for the last usedupload_id
and saves it as the intance variable@upload_id
. -
#next_upload_id ⇒ Object
Returns and saves the next unique
upload_id
in the instance variable@upload_id
. -
#upload_progress(upload_id = nil) ⇒ Object
Get the UploadProgress::Progress object for the supplied
upload_id
from the session.
Class Method Details
.append_features(base) ⇒ Object
:nodoc:
187 188 189 190 191 |
# File 'lib/action_controller/upload_progress.rb', line 187 def self.append_features(base) #:nodoc: super base.extend(ClassMethods) base.helper_method :upload_progress, :next_upload_id, :last_upload_id, :current_upload_id end |
Instance Method Details
#current_upload_id ⇒ Object
Returns the upload_id
from the query parameters or if it cannot be found in the query parameters, then return the last_upload_id
307 308 309 |
# File 'lib/action_controller/upload_progress.rb', line 307 def current_upload_id params[:upload_id] or last_upload_id end |
#finish_upload_status(client_js_argument = '') ⇒ Object
Overwrites the body rendered if the upload comes from a form that tracks the progress of the upload. After clearing the body and any redirects, this method then renders the helper finish_upload_status
This method only needs to be called if you wish to pass a javascript parameter to your finish event handler that you optionally define in form_with_upload_progress
Parameter:
- client_js_argument
-
a string containing a Javascript expression that will be evaluated and passed to your
finish
handler ofform_tag_with_upload_progress
.
You can pass a String, Number or Boolean.
Strings
Strings contain Javascript code that will be evaluated on the client. If you wish to pass a string to the client finish callback, you will need to include quotes in the client_js_argument
you pass to this method.
Example
finish_upload_status("\"Finished\"")
finish_upload_status("'Finished #{@document.title}'")
finish_upload_status("{success: true, message: 'Done!'}")
finish_upload_status("function() { alert('Uploaded!'); }")
Numbers / Booleans
Numbers and Booleans can either be passed as Number objects or string versions of number objects as they are evaluated by Javascript the same way as in Ruby.
Example
finish_upload_status(0)
finish_upload_status(@document.file.size)
finish_upload_status("10")
Nil
To pass nil
to the finish callback, use a string “undefined”
Example
finish_upload_status(@message || "undefined")
Redirection
If you action performs a redirection then finish_upload_status
will recognize the redirection and properly create the Javascript to perform the redirection in the proper location.
It is possible to redirect and pass a parameter to the finish callback.
Example
redirect_to :action => 'show', :id => @document.id
finish_upload_status("'Redirecting you to your new file'")
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/action_controller/upload_progress.rb', line 273 def finish_upload_status(client_js_argument='') if not @rendered_finish_upload_status and params[:upload_id] @rendered_finish_upload_status = true erase_render_results location = erase_redirect_results || '' ## TODO determine if #inspect is the appropriate way to marshall values ## in inline templates template = "<%= finish_upload_status({" template << ":client_js_argument => #{client_js_argument.inspect}, " template << ":redirect_to => #{location.to_s.inspect}, " template << "}) %>" render({ :inline => template, :layout => false }) end end |
#last_upload_id ⇒ Object
Either returns the last saved upload_id
or looks in the session for the last used upload_id
and saves it as the intance variable @upload_id
301 302 303 |
# File 'lib/action_controller/upload_progress.rb', line 301 def last_upload_id @upload_id ||= ((session[:uploads] || {}).keys.map{|k| k.to_i}.sort.last || 0).to_s end |
#next_upload_id ⇒ Object
Returns and saves the next unique upload_id
in the instance variable @upload_id
294 295 296 |
# File 'lib/action_controller/upload_progress.rb', line 294 def next_upload_id @upload_id = last_upload_id.succ end |
#upload_progress(upload_id = nil) ⇒ Object
Get the UploadProgress::Progress object for the supplied upload_id
from the session. If no upload_id
is given, then use the current_upload_id
If an UploadProgress::Progress object cannot be found, a new instance will be returned with total_bytes == 0
, started? == false
, and finished? == true
.
317 318 319 320 |
# File 'lib/action_controller/upload_progress.rb', line 317 def upload_progress(upload_id = nil) upload_id ||= current_upload_id session[:uploads] && session[:uploads][upload_id] || UploadProgress::Progress.new(0) end |