So I was facing a little problem – I had massive file uploads to contend with and there’s nothing worse than not knowing what stage an upload is at, particularly for an impatient user.

I looked around for solutions to file uploads and didn’t like anything I saw, most were flash based and cumbersome – I’m no fan of Flash and I don’t necessarily want to rely on it on my sites.

I also wanted to be able to communicate back to the user on what processes were happening on the server while the file was being processed after upload, it turns out that the file upload progress bar solution I found could very easily be modified to allow a client to get almost-live updates without any complex TCP-tunnels or socket programming.

The solution is a magic-mojo mix of jQuery, Django and the Django cache (use memcached, seriously). The whole thing relies on setting up your own upload handler – here’s what to do:

Step 1:

Read this solution and implement the custom upload handler, integrate it into your site (I placed in a separate file for neatness).

The linked solution essentially writes file upload status data to your cache and creates a view for requesting that data – all you need to do is call it.

Step 2:

Get jQuery timers and add this to your site:

$(document).ready(function() {
          $("#send").click(function() {
                              $(this).everyTime(1000, function(i) {
                                       $.getJSON("http://my-address-here.com/checkprogress?X-Progress-ID={{progress}}",
                                          function(data){
                                              // Do something here!
                                          });
               });
          });
     });

All we’re doing here is telling the button to start a timer and call the update view on our server to work out our progress

Step 3:

Get a progress bar – I particularly liked this one, it was clean and easy to set up – my implementation looked something like this:

<script type="text/javascript">

     function updateBar(values) {
          var l = values.length;
          var tot = values.uploaded;
               
          var perc = (tot / l) * (100.00);
         
          $("#uploadprogressbar").progressBar(perc);
     };
     
     $(document).ready(function() {
         

          $("#uploadprogressbar").progressBar();
         
          $("#send").click(function() {
                              $(this).everyTime(1000, function(i) {
                                       $.getJSON("http://my-address-here.com/checkprogress?X-Progress-ID={{progress}}",
                                          function(data){
                                              updateBar(data);
                                             
                                          });
               });
          });
     });

     
</script>

Now that everything is wired up, why not go a step further and add this little helper function to your view:

def update_progress_direct(request, message):
    from django.core.cache import cache
    try:
        cache_key = "%s_%s" % (request['addr'], request['pid'])
        data = cache.get(cache_key)
        if data:
            data['status'] = message
            cache.set(cache_key, data)
        else:
            cache.set(cache_key, {
                'length': 100,
                'uploaded' : 100,
                'status' : message
            })
    except:
        pass

This will enable you to write directly to the cache where the progress data was held – simply add a status key and look out for it in your jQuery function (the below code was used post-upload, to show the user what was going on):

function updateBar(values) {
          stat = values.status;
         
          if (stat == 'Completed upload... queueing') {
               perc = 40;
               $('#message').text(stat);
          }

          else if (stat == 'Doing something!') {
               perc = 60;
               $('#message').text(stat);
          }
          else {
               perc = 100;
               $("#uploadprogressbar").progressBar(perc);
               $('#message').text("Done...");
               window.location = "http://redirect-your-user.com/"
          }
         
          $("#uploadprogressbar").progressBar(perc);
     };

A few notes:

  • The progressID should uuid encoded – you can use pythons uuid4() function to generate this and pass it to the vuiew as {{progress}}
  • You will need to pass (and keep) the request object to whatever processing function you are using to use the update_progress function
  • Don’t forget to update your URL’s.py to set up your return views! It’s all documented in the orignal solution mention at the start of this post
  • And that’s it, interestingly I found this solution, on the client side, can slow down the browser quite a bit if there’s a large file to upload – I can’t for the life of me figure out why – anyone able to shed light on this would be most welcome to do so in the comments!

    Happy coding,

    Martin