File upload forms that don't suck

Most file upload forms suck. It's not just the lack of design, but also the unintuitive and inflexible interface. There is usually a single file upload field, where you need to select and upload files one at a time. If your connection is slow and you have a bunch of files to upload, it will soon start to be irritating. Hotmail does it that way.

The slightly better approach is to have a list of file upload fields, so you can select the files at once, and be done with it. This method is sometimes assisted by a button to add more fields if you would need that.

But that is so far from what we are used to from the desktop applications. The text that can fit in the file upload field is not a very good representation of the actual file. (Normally something like "C:\Documents and Setti...") And there's just too many buttons.

The idea

Internet Explorer supports the "file" pseudo protocol, which let's us access images locally. By using some more or less clever CSS, we can resize even a large image to a suitable thumbnail. Since the file is local, we don't need to worry about the waste of bandwidth this would normally imply.

If we check the extension of the file, we can also use CSS to show the user nice icons of the most popular file formats someone might want to upload. Add the filename itself so we can tell files of the same type apart, and we have a pretty good graphical and textual representation of the files.

The file upload field itself can be hidden, since it's pretty useless as a user interface. We will still keep it in the DOM structure, though, so the file will actually be uploaded when the form is submitted.

The solution

I implemented this as an unobtrusive Javascript. That means the only thing that needs to be changed in the HTML is the class name of the original file upload field. Just add the script and the stylesheet to the header and you're set. PHP users should note that you need to put a pair of square brackets after the name attribute of the file upload field tag, or PHP won't be able to handle the upload properly. Like this:

<input type="file" class="wwIconified" name="someName[]" />

This also means the script will degrade gracefully if Javascript is not available. The user will then be presented a Hotmail-style single file upload form.

The script will check for the existence of a ul-tag with a specified class name in the same form as the file upload field. If it does not exist, it will be created. This list is used as the base for the file icons.

An event handler attached to the on-change event of the original file upload field will be fired as soon as the user selects a file for upload. This event handler will do most of the magic.

First, it will create a base node to place the icon in. The value of the file upload field is the path to the local file. The extension of the file is extracted, and used to determine what kind of icon to create.

If the file type is recognized as an image format, an image is created, with the src-attribute set to "file:///" plus the file path. Otherwise the file extension is set as the CSS class name of a div-element. The first n characters of the filename are also added to the base node. (Too long names would easily break any design.)

Next, a "remove"-button is added. It's nice to have in case you would pick the wrong file. I will simply remove the complete base node from the DOM structure.

To make it all functional, the original file upload field must be moved to the base node and replaced with a copy. I had some trouble with this at first, since copying the original field to the base node was working fine in Firefox, but not in Explorer.

It turns out Explorer is highly paranoid about the value of file upload fields. You are not allowed to set them, nor to clear them by Javascript. Even when copying the entire input element node, the value is cleared. I respect that, since it prevents possible attacks uploading files from the user without him/her selecting them manually.

Instead, a copy of the original file upload field is created and inserted next to the original. The original itself is then moved into the base node, where it is hidden by a CSS rule.

Wanna try it? Check out this live example!

Limitations

There are some limitations to this technique. First of all, the "file" pseudo protocol does not work in Firefox, if the document itself is not stored locally, due to possible security issues. (Interestingly enough, for the same reasons, Internet Explorer will not run the script if the document is stored locally.)

That means a lot of the users will not be able to enjoy the wonderful world of pre-upload thumbnails. You'll need to be careful to keep them in mind when writing your stylesheet. But you already do that, don't you?

Obviously, this will only work with Javascript enabled, or there will only be a single file upload field. You might want to hack the script to allow more file upload fields for the non-Javascript users.

The script as it looks now, can only handle a single file upload list in each form. That might be an issue if you have more than one type of files to upload. I would suggest that you let the user chose what group of files he/she is working with, so he/she can upload them in batches when needed.

There is also a quite important factor to consider: The total file size. That is of course not an issue specific to this solution. But because of how simple it is, a user might be tempted to abuse the solution and upload dozens and dozens of large photos at once. -Which will naturally fail, due to time-outs. That might be solved with some more scripting, though.

Files

You are welcome to use the code as you like. (No license involved.) It would, however be nice to get some feedback. victor (insert-curly-symbol-here) topmost.se