Ajax file uploading
June 26th, 2006I was messing around today with trying to duplicate the way that gmail supports file uploading in the background. I didn’t reverse engineer anything but worked solely from what I figured they must be doing based on what I saw of their interface.
I was able to duplicate the gmail behavior in Firefox and Opera 9 (<9 has some weird IFRAME support that I have never been able to figure out). Basically, you drop an input type=’file’ anywhere on your page and then in response to an onchange, do a cloneNode(true) and insert the clone into a form on a hidden iframe. You can then submit the iframe. The cloneNode support allows us to work around the problem where the individual filename/path cannot be set on this control due to security restrictions. It works pretty well.
Internet Explorer is another issue. When trying to insert the cloned node into the iframe document, it would always signal an “Illegal Argument” (RANT: I just love Internet Explorer’s error messages). I finally took this to mean that it does not supporting inserting an element from one DOM document into another. I tried the importNode method just to find out it isn’t there in IE. I was pretty frustrated at this point and decided to browse to gmail with IE to see if Google had been able to solve the problem. No dice. Their file upload mechanism for IE is completely different than it is for non IE. I figure that they must be using some barely documented IE method to do their magic, but I don’t know what it is.
Anyway, what I finally came up with was putting the file input control directly in an iframe on the page and forgo the copying. I apply some dynamic formatting to make sure the iframe is sized to just slightly larger than its contents and you can’t tell there is an iframe there. When the onchange event of the file input is raised, I hide the iframe and unhide the status/cancel buttons. I then hook the iframe’s onload event and submit the encapsulated form. While the iframe form is submitting, a poller kicks off to run out to the server periodically and update the displayed progress information on the main page. It’s a little kludgy but is nicely encapsulated in a widget and it works pretty much without change on the major browsers (sans Safari for the moment — I’m having some other problems on the Mac front).
So my final analysis: If you’re the size of Google, go ahead and support a different mechanism for IE, but I would rather make it the same all around. Therefore, I think this approach is a good middle of the road.