diff options
Diffstat (limited to 'site/src/docs/upload_progress.page')
-rw-r--r-- | site/src/docs/upload_progress.page | 136 |
1 files changed, 0 insertions, 136 deletions
diff --git a/site/src/docs/upload_progress.page b/site/src/docs/upload_progress.page deleted file mode 100644 index b40eaef..0000000 --- a/site/src/docs/upload_progress.page +++ /dev/null @@ -1,136 +0,0 @@ ---- -title: Upload Progress -inMenu: true -directoryName: Upload Progress ---- - -h1. Mongrel Upload Progress Plugin - -One of the really nice things about Mongrel is its simplicity. It's very easy -for someone to take and extend for their own needs. The Mongrel Upload -Progress plugin is an example of how I'm able to extend the Mongrel HTTP -Request object and provide near-realtime progress updates. - -The reason why this is a challenge, is because web servers usually gather the -HTTP request, send it on to the web framework, and wait on a response. This is -fine for most requests, because they're too small to cause an issue. For large -file uploads this is a usability nightmare. The user is left wondering what -whether their upload is going through or not. - -To do this, I've written a Mongrel handler that hooks into some basic Request -callbacks. To use it, you need to install the gem, and create a small config -file for it: - -<pre><code>gem install mongrel_upload_progress - -# config/mongrel_upload_progress.conf -uri "/", - :handler => plugin("/handlers/upload", :path_info => '/files/upload'), - :in_front => true - -# start mongrel -mongrel_rails start -d -p 3000 -S config/mongrel_upload_progress.conf -</code></pre> - -That config file tells mongrel to load the Upload handler in front of all other -handlers. <code>:path_info</code> is passed to it, telling upload_progress to -only watch the /files/upload action. There are two more parameters that I'll -get into later: <code>:frequency</code> and <code>:drb</code>. I'm using Rails -as an example, but this should work with any Ruby framework, such as Camping or -Nitro. - -Now that Mongrel is set up, let's create a "basic upload -form":/docs/upload_progress_form.rhtml. If you -look closely you'll notice a few things: - -* A unique <code>:upload_id</code> parameter must be sent to the upload_progress handler. This is so requests don't get mixed up, and the client page has an ID to query with. -* The <form> tag is targetted to an iFrame to do the uploading. Certain browsers (like Safari) won't execute javascript while a request is taken place, so this step is necessary. -* There is a little "javascript library":/docs/upload_progress_javascript.js being used. This handles the polling and status bar updates. -* Notice the form's action is file/upload, just like the upload_progress handler. - -The "Rails controller -actions":/docs/upload_progress_rails.rb for this are very -simple. The upload form itself needs no custom code. The upload action only -renders javascript to be executed in the iFrame, to modify the contents of the -parent page. The progress action is a basic RJS action that updates the -current status. Most of the guts of this are implemented in the javascript -library. - -Here's what happens when you submit the form: - -* The UploadProgress class creates a PeriodicalExecuter and gets ready to poll. -* The browser initiates the upload. -* Every 3 seconds, the PeriodicalExecuter calls the RJS #progress action and gets back the current status of the file. -* Once finished, the iFrame calls <code>window.parent.UploadProgress.finish()</code>, which removes the status bar and performs any other finishing actions. - -How's this work with a single Mongrel process if "Mongrel synchronizes Rails -requests":http://david.planetargon.us/articles/2006/08/08/why-you-need-multiple-mongrel-instances-with-rails? -It's actually very careful about locking, synchronizing only the bare minimum. -The whole time that Mongrel is receiving the request and updating the progress -is spent _in_ Mongrel, so it can happily serve other requests. This is how the -RJS action is able poll while it's uploading. - -This is fine and dandy, but not too many sites run on a single Mongrel. You'll -quickly run into problems with multiple mongrels since only one Mongrel process -knows about the upload. You'll either have to specify a specific mongrel port -to communicate with, or set up a dedicated mongrel upload process. The third -option, is use DRb. - -<pre><code># config/mongrel_upload_progress.conf -uri "/", - :handler => plugin("/handlers/upload", - :path_info => '/files/upload', - :drb => 'druby://0.0.0.0:2999'), - :in_front => true - -# lib/upload.rb, the upload drb server -require 'rubygems' -require 'drb' -require 'gem_plugin' -GemPlugin::Manager.instance.load 'mongrel' => GemPlugin::INCLUDE -DRb.start_service 'druby://0.0.0.0:2999', Mongrel::UploadProgress.new -DRb.thread.join</code></pre> - -Now in addition to starting mongrel, you'll need to start the DRb service too: - -<pre><code>ruby lib/upload.rb</code></pre> - -The Rails app should work the same as before, but now it is using a shared DRb -instance to store the updates. This gives us one other advantage: a console -interface to the current uploads. - -<pre><code># lib/upload_client.rb, a simple upload drb client -require 'drb' -DRb.start_service - -def get_status - DRbObject.new nil, 'druby://0.0.0.0:2999' -end - -# typical console session -$ irb -r lib/upload_client.rb ->> uploads = get_status ->> uploads.list -=> [] -# start uploading in the browser ->> uploads.list -=> ["1157399821"] ->> uploads.check "1157399821" -=> {:size=>863467686, :received=>0}</code></pre> - -Using DRb gives you a simple way to monitor the status of current uploads in -progress. You could also write a simple web frontend for this too, accessing -the DRb client with Mongrel::Uploads. - -One final note is the use of the <code>:frequency</code> option. By default, - the upload progress is marked every three seconds. This can be modified - through the mongrel config file: - -<pre><code>uri "/", - :handler => plugin("/handlers/upload", - :path_info => '/files/upload', - :frequency => 1, - :drb => 'druby://0.0.0.0:2999'), - :in_front => true</code></pre> - - |