HTML5 File API + XmlHttpRequest = SWFUpload, now what?
So what should you do if you decided to ditch SWFUpload and embrace HTML5 solution for ajax file upload? In honor of the releases of Firefox 3.6 (with File API), I decided to just that in the GFX Firefox promotion site. (sounds perfectly reasonable to use Fx-only technology on such site, right?) Firefox now gives us the bricks (File API, binary XHR, and File drag-drop), but to build a house it still takes some work. The Mozilla Hacks demo works great but to work with the existing site I need:
- To create a SWFUpload-like button that opens file selection dialog instead of HTML form file input.
- To send the file as a ordinary file upload form – no need to change php backend to handle raw post
- Send the file by jQuery.ajax, firing global callbacks
I came up with these methods to solve them:
SWFUpload-like button
From the JavaScript almighty ppk we know that file input from control is almost impossible to style. The solution outlined on that page only tell us how to replace the control with images using opacity: 0 trick. In order to fit the clickable area into my 65×65-px button, I have to enlarge the control and confine it into a <div> with overflow: hidden.
The actual hard part is how to enlarge the control. It turns out in Firefox it ignores CSS specified height and width; the only thing left for you to set is font-size propriety.
Another CSS properity it ignores is the cursor. Firefox puts the “browse” button at the very right, so you might what to make sure the arrow cursor, instead of text-input cursor, shows.
Simulate form upload
On Mozilla Hacks demo, the file is sent as raw HTTP POST/PUT data. To process such input, one must craft the server-side script all over, without any upload-handling library that I’ve known of. So we go back to client-side solution: the question now effectively becomes how to implement RFC 1867 format to your (raw) post data. I’ll simply post my code snippet below – turn out to be quite self-explanatory. *
var bd = 'gfx-xhrupload-' + parseInt(Math.random()*(2 << 16)); var data = '--' + bd + '\n' + 'content-disposition: form-data; name="Filedata";' + ' filename="' + files[0].name + '"\n' + 'Content-Type: ' + files[0].type + '\n\n' + ev.target.result + '\n\n' + '--' + bd + '--';
Where ev.target.result is the binary string represent the file, access using FileReader object, and files is the FileList object.
Send the file via jQuery.ajax
Update: Just found an easy way to do this right instead below: overwrite
xhr.sendwithxhr.sendAsBinarysince it’s seems all right to send text data in binary mode.*if (window.XMLHttpRequest && XMLHttpRequest.prototype.sendAsBinary) { XMLHttpRequest.prototype.send = function (data) { return this.sendAsBinary(data); }; }
You might think this is the easiest part, but in reality the normal $.ajax won’t work even if you set the correct Content-Type, which, according to RFC 1867, is 'multipart/form-data, boundary=' + bd.
What went wrong? The problem is jQuery hard-coded xhr.send(data) in it’s function, and it does not send binary data. I resolved the problem by actually copying jQuery code, “patch” it, and overwrite the original $.ajax function. I added a “binary” option for the purpose; the function would switch to xhr.sendAsBinary(data) if the option is set to true.
Bonus: File drag and drop
What SWFUpload doesn’t do is to make the control accepts files by droping them on it. With Firefox 3.6 you could to that, but you cannot hook the drop event on the <input> itself. Frankly speaking, the button would be to small to aim, so for the sake of better UX, a larger overlay, like the one in Mozilla Hacks demo, should be made. Do remember that hiding the overlay as soon as dragleave fires will stop drop event from firing though – took me a day to figure that out.
Also, keep in mind your transparent button will accept file dropping if the user had installed dragdropupload add-on.
Conclusion
With all these research, looks like it’s actually possible to write a SWFUpload.js without Flash. The library would support anything SWFUpload does, except cursor choices on the button. For my project, I didn’t craft a new XHRUpload() object to replace SWFUpload() because I don’t need that much of functionality, yet it’s something can be done.
It struck me that HTML 5 is practically involves around what Google want to do things w/o Flash. StreetView? We got WebGL. GMail attach. upload? Got File API.
HTML 5. Exciting new world.
For complete code on the implementation, stay tuned and check code here once it went live. For the site, we have not deliver English version yet, but feel free to play around with buttons and symbols – you should be able to understand it without reading any Chinese.
Note: According to the MIME spec, the filename should be encoded according to RFC 1522 (“quoted-printable”) if it contains characters other than US-ASCII. Personally I replace these characters with underlines instead – name = name.replace(/[^\x20-\x7E]/g, '_');.
More update: XMLHttpRequest.prototype.sendAsBinary instead of (new XMLHttpRequest()).sendAsBinary


[...] This post was mentioned on Twitter by 柏強, Po-chiang Bob Chao. Po-chiang Bob Chao said: [HTML5] @timdream, developer behind MozTW's grass-root marketing platform gfx.tw, share his experience on File API http://ping.fm/W53sF [...]
[...] 這年頭上傳檔案要是沒辦法一次選很多個檔案上傳,可能會有很多人幹樵;從最老牌的swfupload到HTML 5 File API都支援,不過Flash吃資源,而且swfupload的更新速度老實說不快;HTML 5則不是每家瀏覽器都支援。於是,TinyMCE的作者們就寫了plupload,只要瀏覽器支援Google Gears、Flash、Sliverlight、Yahoo Browser Plus,或是HTML 5 File API的其中任何一項,就可以一次選擇很多檔案上傳! [...]