YUI Uploader Implementation

Image Uploading using YUI Uploader Widget

A few days ago the YUI team released the latest and greatest version of the YUI Library, YUI 2.5.0. Along with the usual round of bugfixes and speed improvements this release came with several new components. I personally was the most excited about the Uploader component, as the technology that powers it also underpins the upload process on Yahoo! Video. I didn’t write the upload pages but I’m expected to be able to provide bugfixes and enhancements to those pages, so what better way to learn how they work than from the ground up with the Uploader?

Yes, I know there’s quite a few better ways. This was the most fun sounding way. What can I say, I like 4 day projects in my spare time instead of just reading the existing code. Besides, I wanted a better understanding of it from a lower level than just how the upload code on VYC works.

As mentioned in the YUI Blog posting about 2.5.0, the Uploader is what not only powers VYC uploads but Flickr uploads as well. Flickr exposes more of it due to their allowing of multiple files uploaded at once. That’s not really very easy to do on VYC due to the much larger file sizes being used. Still, it’s there. I promise.

I’ve had a free-for-all, open to anyone image uploader chilling out for quite some time at http://tivac.com/upload/. I wrote it to solve a problem, namely the other image uploading services available at the time sucked. It was just a quick little thing, with some basic JS that would hide the form element and create a new one so you could upload more than one file at a time. When you were done it would barf out thumbs of the images along with some common types of link code for forums and the like. Nothing fancy at all. Well, the new YUI Uploader seemed pretty much tailor-made to work instead of creating new form elements.

Here’s a good representation of my thought process while I contemplated redoing the image uploader.

Method Select multiple files from one dialog? Progress feedback available for updating the UI? Smart enough to not totally hose the browser while uploading? Able to dynamically update UI on completion?
Form + JS No No No Sorta
Uploader + JS Yes Yes Yes Yes

I think that paints a pretty compelling picture of why I’d go with the YUI lib over the original solution. Aside from requiring Flash 9 and not working on the latest OSX because Adobe and Apple are feuding, there’s really no downside. It’s provably better in every way. Since this is just a junky little personal project and not something important, I don’t even provide a fallback HTML form any more. That’s just how I roll.

Actual implementation was pretty basic. The YUI documentation is excellent as always. It more or less started life out as a copy of their Simple Upload Example and then I hacked the crap out of it. The flow ended up being something along the lines of the following.

  1. Browse for image files.
    upload step 1
  2. If you selected one you don’t actually want to upload, clicking it’s filename will remove it.
    upload step 2
  3. Upload the files, watch all the fun progress bars whiz around.
    upload step 3
  4. As each file completes, its filename is replaced with a thumbnail and various different pre-filled link codes for HTML and forums.
    upload step 4
  5. You can also hit the Export button to just get a list of the URLs of the uploaded images.
    upload step 5

It sounds complicated because I’m an engineer and don’t explain things well. The cool nerdy thing about this is how very event-driven it is. For example, take a look at this code chunk.

 

//certain things can only happen once the SWF is ready to rock
this.uploader.addListener('contentReady',   this.swfReady);
//event handlers
this.uploader.addListener('fileSelect',         this.onFileSelect);
this.uploader.addListener('uploadStart',        this.onUploadStart);
this.uploader.addListener('uploadError',        this.onUploadError);
this.uploader.addListener('uploadProgress',     this.onUploadProgress);
this.uploader.addListener('uploadCancel',       this.onUploadCancel);
this.uploader.addListener('uploadComplete',     this.onUploadComplete);
this.uploader.addListener('uploadCompleteData', this.onUploadCompleteData);

There’s an event fired by the flash object for pretty much everything you could want. The only event I found myself wishing it supported was a “allUploadsComplete” method. As it was I had to create a cache of all the file ids being uploaded, and in my handler for uploadComplete I would remove each id from the cache. If there’s no more files left to upload, hey presto we’re done! A little roundabout, especially when having to watch out for files that were selected and then removed.

The other fun thing I decided upon was that instead of using innerHTML and getting back big chunks of HTML from the server (wrapped in JSON, of course) I’d instead use skeleton structures hidden in the DOM. A quick

YUD.get('upload_skeleton').cloneNode(true);

and you’ve got yourself a nice chunk of HTML just waiting for some delicious data to be inserted. The actual insertion is pretty boring DOM traversals, but by using a skeleton as the base it avoids heavy node creation/appending or a massive innerHTML dump. The page’s DOM is a little heavier because it contains these stubs, but there’s only two of them and page weight isn’t a big concern for this project.

The progress meters are basically a total ripoff of Flickr, I feel a little bad about it but I really liked their approach of using a background image and just changing the x offset each time uploadProgress was called. It’s so simple and to the point that I didn’t see any reason to make it more complicated.

So that all works fine, but I was worried that if I didn’t copy the URLs correctly right away the images would be lost forever. That meant that I had to write a little PHP script that would output linked thumbs of all the images that had been uploaded. Since this is a totally unrestricted image uploader there’s been a few instances of people uploading porn and the like. I’d rather not deal with that, so there’s a little delete script as well. It’s protected by a .htaccess/.htpasswd basic auth setup. Nothing fancy, but it keeps other people from being able to delete my images.

image browser

The images are laid out in a big floating grid using Hedger’s very awesome work on getting display: inline-block to work across browsers. Item List Grid : Practice with display:inline block across browsers. It’s a technique we also used extensively on VYC so it was the first thing that came to mind when just floating a bunch of images that weren’t the same height went all crazy.

Oh, right. All the icons are the insanely awesome work of Mark James, specifically his Silk set of icons. I’m sure you’ve seen them used pretty much everywhere. There’s a good reason for that, they rock.

I’ve zipped up the source for anyone who wants it, it’s all pretty well-commented. You will have to handle the file permissions yourself though. UPDATE: Sitzmar wanted me to point out that all the paths are hard-coded for my uploader.  He says I should be ashamed, I say he should shut up.  Also I don’t think I included the .htpasswd (obviously) or the .htaccess.  Those are easily set up, a quick search on the internet will answer all questions.

upload.zip

9 Responses to “YUI Uploader Implementation”

  1. Etienne:

    I was waiting for this ! Thanks a lot. Just a point, it works fine on your site. On mine, there is no action when clickin the “browse” button. I verified paths, rights and so on …

    March 15th, 2008 at 3:48 am

  2. Etienne:

    DAmn’ I’m DUmb … some files are referenced with a U in uppercase. It should be cool to fix this :)
    Thanks a lot ! I keep testing !

    March 15th, 2008 at 4:21 am

  3. Etienne:

    Thanks again for that nice tool ! That’s me again … the upload process is ok, files are nicely uploaded on the server.
    The only thing is the process doesnt seem to complete it stays stuck without displaying what it does on your site.
    Any idea ?

    March 15th, 2008 at 11:00 am

  4. Pat Cavit:

    Not sure what the problem is there Etienne, since this is doing file uploads there’s a lot of different things that could be causing those issues.

    As for nothing when you hit browse, that’s likely due to a missing crossdomain.xml file at the root of your site. Here’s mine, you can just copy it. crossdomain.xml

    Process not completing sounds like the Flash can’t talk to the JS properly. I’d search around at the YUI group (http://groups.yahoo.com/group/ydn-javascript/) and see if anyone has reported similar problems there. If not go ahead and post, they’re super helpful.

    March 15th, 2008 at 11:07 am

  5. Etienne:

    I’m Dumberer than Dumber … that was a gd problem (at the resize time, thanks to the error log).
    It’s fixed.
    For the crossdomain, I simply took the whole yui on my server.
    Thanks a lot Pat, I think your kit is gonna be useful in humanitary projects !

    Etienne

    March 15th, 2008 at 11:23 am

  6. KB:

    Great implementation of the UI loader! It seems that I have the same problem as Etienne, after the upload process is completed nothing happens. It doesn’t show the overview page with the uploaded images and the url list. Maybe Etienne you can tell me how you solved the problem. Also I would like to know how to enable error logging. Thanks a lot in advance.

    March 21st, 2008 at 2:50 pm

  7. Etienne:

    Well, I didn’t really soved it but now, most of my images are displayed.
    Phase one, I fixed the GD problem … installing it (shame on me).
    Phase two, I resized the memory limit in php .. because the createfromjpeg function is really heavy in memory (remember height x width x depth).

    By the way the files are always uploaded but the thumbnail process may crash. I depends on what you wanna do with it. If you can, just take a look at your error log.
    I only wanted to get a nice uploader, so I’m really happy :)

    March 21st, 2008 at 3:07 pm

  8. Etienne:

    yess … I used your beautiful tool + dicom lib so I can manage huge dicom files.
    Thanks a lot Pat !

    March 22nd, 2008 at 4:05 pm

  9. Vayu:

    I cant get it to work either. Am wondering if its something to do with php settings. I can get it to work on my local computer, but not website server.

    If I use your upload.php file it works great, but not when I use on my server.
    Good:
    this.uploader.uploadAll(’http://tivac.com/upload/upload.php’, ‘GET’);

    Bad:
    this.uploader.uploadAll(’http://flashkompagniet.dk/uploader/upload.php’, ‘GET’);

    Any clues to the puzzle?

    Vayu

    April 22nd, 2008 at 7:52 am

Leave a Reply

Comments will be sent to the moderation queue.