File uploads to Amazon S3 the AJAX way thanks to Dojo

File uploads are finally deployed! You can now specify in MyOwnDB that a detail of your entity is of the type "file". This will let you attach a file to an entry, and subsequently replace or delete it. This required some changes in the application, more specifically in the way the forms are submitted back to the server. For a file upload to work fine, you have to send it with the enctype attribute set to "multipart/form-data". But that's not all! You cannot just serialize the form in javascript like you can do with other input types because you cannot access the file from Javascript. The reason for this is security.

Dojo to the rescue

Luckily, the Dojo javascript toolkit has a workaround that works amazingly well, and is really easy to put in place. Dojo is facilitating all XMLHttprequest thanks to its dojo.io.bind method, which I discovered thanks to an article correctly title "Introduction to dojo.io.bind". This article shows that dojo.io.bind can be used to send simple GET or POST request, but also to submit forms. At first, I thought it didn't support file uploads (as stated in the document), but it does amazingly well. And, best of all, it is cross browser.

Installing Dojo in Rails

I downloaded the AJAX edition and uncompressed it in the public/javascript/dojo directory of the rails application. I then put the following code in my layout:



Of course it is longer, but you have file uploads now! Some remarks: - I had to put the method in the form tag to make it work with IE. For Firefox, passing it only as a parameter to do.io.bind works. - rails' remote_forms set the HTTP header 'X-Requested-With': 'XMLHttpRequest' and you can then check it in your controller with request.xhr?. This doesn't work anymore as it is not possible to set HTTP headers with the IframeIO transport. - I didn't find a way to read HTTP headers of the response - I sometimes had to put all the code of the dojoformfunction(f) function above in the onsubmit attibute of the form, as it refused to work as illustrated above. I didn't find the reason of this.

How does it work?

Dojo transparently creates an IFRAME, and submits the form through that IFRAME. That way, the browser itself is submitting the file. This has the side effect that you loose some functionality compared to XMLHttpRequests, like illustrated by the last remarks above. I'm really happy with the result and the simplicity of the code. It allowed me to not touch any code outside of the form submission, and treat all form in the same way. When I started coding this, Dojo was the only solution I found, but Yahoo!'s YUI lib has gained the same functionality with its connection manager, taking the same approach of creating an IFRAME.