As I discussed in a previous post, one of my first projects at Coursera was implementing social profiles. A big part of our motivation for adding user profiles is to add a sense of community and intimacy to classes, particularly in the place where students interact with each other the most - our forums. The forums were just long streams of text before, and there was one little thing we could add that would break up those streams of text and instantly make them feel like a social experience: user photos!
So, in our new profile editor form, the photo upload area is at the very top, and it's the first part of the form that I worked on. Based on my experience from EatDifferent, I knew that photo upload (or file upload generally) wasn't an easy thing to get working across all the different browsers, and I decided immediately that I did not want to rewrite cross-browser file upload from scratch. I started playing around with the popular jQuery upload library, but then I realized that I also had to solve the second part of the upload equation: once I had the photos, where would I store them on the server, how would I resize them, and how would I get them back out? We store many of our assets in Amazon S3, so I started looking at how to use Python to do image manipulation and S3 upload.
Client-side Uploads with Transloadit
Before I got too far down that path, I remembered that there's a new startup that would do all of that for me: Transloadit. They take care of file manipulation (like image resizing and cropping) and file storage (like to S3 and Youtube), and best of all, they provide a jQuery plugin that will do all of that for you, with no server-side scripting on your side. (There's also a very similar startup FilePicker.io, but I happened to meet the co-founder of Transloadit while wandering the streets of Lyon, France, so of course, I was a bit biased in my selection.)
The Transloadit Plugin
Transloadit designed their plugin so that you attach it to a form, and then it listens to the submit, processes the file inputs, optionally throws up a progress indicator, and notifies you with JSON results when it's done. That wasn't quite the experience that I wanted for our profile form, however. I wanted users to select a file to upload and immediately see it uploaded and displayed, while they continue filling out the form. I changed the Transloadit plugin so that it attaches to any DOM node, and processes the file inputs inside whenever a change event is fired for them.
My Bootstrap Plugin
Both because I didn't like the modal nature of the Transloadit progress indicator and because I wanted a more cohesive look & feel to the upload, I came up with my own Bootstrap-based HTML and CSS for the uploader, which includes a progress bar and a hack for actually letting users click a nice pretty button instead of the default (ugly) file input control.
To tie the HTML, CSS, and JS together, I wrote a small Twitter Bootstrap plugin. In that file, I call the Transloadit plugin on the form, specifying options to wait for results, disable the modal, not submit the form after file upload, and also defining various event callbacks. When the user first picks a file, I change the button text and show the progress bar, and as the upload progresses, I increase the progress bar width. Finally, when the file has uploaded, I fade the progress bar and display the image for the user to see. If the user wants to replace the image, they re-click the button and the progress begins again. Behind the scenes, I store the generated S3 URLs (for 3 different resized versions plus the original) in hidden inputs in the form, and that's what goes into our database.
You can see that flow here:
This isn't the perfect photo upload UI, of course. I'd love to give users the ability to crop the photo on the client (like Twitter has now implemented in their avatar upload UI), and I think I could make it more obvious how to change the photo. But, it has served us well enough so far for the profile form, so we're now using the plugin in our admin interface as well, for accepting all the course, university, and instructor media assets.
To make it easier for others to use Transloadit together with Bootstrap like I have, I've put all the code into a git repository along with examples, which show how to use it for both photos and files.