2012-11-27

Ajax file upload progress

For I project I work on, we needed file upload progress bars. This is a hell, as everybody who tried knows. First time I tried it used an external CGI helper that saved the file to my temporary file, with a special id, and using ajax I was polling for the progress. Inefficient, prone to errors, and even to fill your tmp.
Of course there are more complete solutions, but the basic idea on most of them is the same: upload it on one place, sneak into the progress using AJAX on another place.
But HTML5 was here to help us. Wasn't it?

Introducing HTML5 + jQuery + Javascript upload progress!

First the how to use it:
  1. Download it from https://github.com/davidmoreno/jquery-file-upload-progress
  2.  Load jQuery and uploadProgress.js on your html.
  3. Add this:
    $('form').uploadProgress( { onProgress: myprogressbar } )
  4. Setup your myprogressbar(progress) function, that will be called as file is uploaded.
  5. Done
This uploadProgress function might be something as simple as a jQuery UI progress bar, and then:
uploadProgress = function(p){
   $( ".selector" ).progressbar({ value: p });
}

How it works.

At birds view this plugin uploads the form using XHR, tracks the progress signal (new in HTML5), and when finished, if it cans, replaces current content with the XHR returned content.
Many, really many, things can fail when doing the upload, for example the replacement of the page fails, or the upload fails, or it succeds but returns an error page. And last, but not least, the progress signal might be missing. On all those situations we should at least make it work as in good ol' times, and just forget about the progress. I'm looking at you IE.

How it really works.

Hooking the progress.

jQuery does a great job allowing expansion and customization. We use this availability to replace the built in xhr method on the .ajax function to add the progress bar. It will call the opts.onProgress method with a percentage of completion.
It also hooks other methods to the AJAX query: onBeforeSend, onComplete, and onError.
Finally but not last, its important to send the proper data: new FormData(form); is your friend here.

Getting the answer.

As you know using XHR means AJAX, which in turn is not just loading a page. IT returns whatever this form wanted to do. Normally to show a new page. So I prepared a special function that takes that answer and just replaces current content with that. Its dirty, not always work, but normally is what you want: just show the new page. 
It can fail if the CSS is diferent (for example an error is returned), and on some other conditions, so some work could easily improve the behaviour, but normally just works. 
If it can not replace the contents, it just do a GET on the POSTed page. If your POST just changes the state of that page, and a reload shows that change (which is algo quite normal), it will also work.
This is implemented at line 18.
This are just the two options I'm using, but actually when you decorate your form with $.uploadProgress, you can also set an onComplete or onError to do different things when completed.

Pros & cons

Pros
  • Just works
  • Easily customizable
  • If something fails, normal post behaviour is preserved
  • Uses jQuery
Cons
  • Delicate, not work on IE
  • Hack to show AJAX sent post. May fail and not show progress.
  • Uses jQuery

Clone it!

To clone it:
git clone git@github.com:davidmoreno/jquery-file-upload-progress.git 

Pull request are welcome!

Thanks

No comments:

Post a Comment