{"id":134,"date":"2011-05-10T09:30:32","date_gmt":"2011-05-10T15:30:32","guid":{"rendered":"http:\/\/timothypoon.com\/blog\/?p=134"},"modified":"2016-02-19T09:05:53","modified_gmt":"2016-02-19T15:05:53","slug":"ajax-uploading-with-html5s-file-api","status":"publish","type":"post","link":"https:\/\/timothypoon.com\/blog\/2011\/05\/10\/ajax-uploading-with-html5s-file-api\/","title":{"rendered":"AJAX Uploading with HTML5&#8217;s File API"},"content":{"rendered":"<p>Prior to HTML5 and its <a href=\"http:\/\/dev.w3.org\/2006\/webapi\/FileAPI\/\">File API<\/a>, getting media from a user was a somewhat sordid affair. You generally either had a static page with an &lt;input&gt; element of a &lt;form&gt; posting to a processing file or utilized some third party script that relied on a <a href=\"http:\/\/www.uploadify.com\/\">Flash bridge<\/a> or <a href=\"http:\/\/www.openjs.com\/articles\/ajax\/ajax_file_upload\/\">&lt;iframe&gt; shenanigans<\/a> to asynchronously get the file where it needed to go.<\/p>\n<p>This leads to things like <a href=\"http:\/\/html5demos.com\/file-api\">this demo<\/a> and how you can <a href=\"http:\/\/gmailblog.blogspot.com\/2010\/04\/drag-and-drop-attachments-onto-messages.html\">drag &#8216;n drop (&#8216;n upload)<\/a> files with Gmail. Seeing as how I was tired of doing the aforementioned Flash or &lt;iframe&gt; bullhonky, I decided it was time to get the ball rolling for myself.<\/p>\n<h2>Accessing the Files<\/h2>\n<p>It&#8217;s actually surprisingly simple to see what a user has chosen to upload in a &lt;input&gt; (of a file type, of course). You can access all of the vital pieces of information through JavaScript once they&#8217;ve been selected, and I say &#8220;they&#8221; because if you specify the <strong>multiple<\/strong> attribute in the &lt;input&gt;, you can then select multiple items:<\/p>\n<div class=\"codecolorer-container javascript railscasts\" style=\"overflow:auto;white-space:nowrap;width:100%;\"><table cellspacing=\"0\" cellpadding=\"0\"><tbody><tr><td class=\"line-numbers\"><div>1<br \/>2<br \/>3<br \/>4<br \/>5<br \/>6<br \/>7<br \/>8<br \/>9<br \/><\/div><\/td><td><div class=\"javascript codecolorer\">document.<span class=\"me1\">getElementById<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">'file-input-element'<\/span><span class=\"br0\">&#41;<\/span>.<span class=\"me1\">onchange<\/span> <span class=\"sy0\">=<\/span> <span class=\"kw1\">function<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw1\">if<\/span><span class=\"br0\">&#40;<\/span><span class=\"kw1\">this<\/span>.<span class=\"me1\">files<\/span>.<span class=\"me1\">length<\/span> <span class=\"sy0\">&lt;<\/span> <span class=\"nu0\">1<\/span><span class=\"br0\">&#41;<\/span> <span class=\"kw1\">return<\/span> <span class=\"kw2\">false<\/span><span class=\"sy0\">;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw1\">for<\/span><span class=\"br0\">&#40;<\/span><span class=\"kw1\">var<\/span> i <span class=\"sy0\">=<\/span> <span class=\"nu0\">0<\/span><span class=\"sy0\">;<\/span> i <span class=\"sy0\">&lt;<\/span> <span class=\"kw1\">this<\/span>.<span class=\"me1\">files<\/span>.<span class=\"me1\">length<\/span><span class=\"sy0\">;<\/span> i<span class=\"sy0\">++<\/span><span class=\"br0\">&#41;<\/span> console.<span class=\"me1\">log<\/span><span class=\"br0\">&#40;<\/span><span class=\"kw1\">this<\/span>.<span class=\"me1\">files<\/span><span class=\"br0\">&#91;<\/span>i<span class=\"br0\">&#93;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><br \/>\n<span class=\"br0\">&#125;<\/span><span class=\"sy0\">;<\/span><br \/>\n<span class=\"coMULTI\">\/**<br \/>\n&nbsp;* name - file name<br \/>\n&nbsp;* size - file size in bytes<br \/>\n&nbsp;* type - file MIME type<br \/>\n**\/<\/span><\/div><\/td><\/tr><\/tbody><\/table><\/div>\n<p>Aside from the attributes, there are also some pretty sweet functions like <em>getAsDataURL()<\/em> and <em>getAsText()<\/em> which can have some cool uses (such as dynamically loading an image as a CSS background image in data form), but we&#8217;ll have to save that for another day. All we need right now is access to that files array.<\/p>\n<h2>Uploading the Files<\/h2>\n<p>It starts pretty much the same as any other AJAX data transfer, which is with a <strong>XMLHttpRequest<\/strong> object. The only difference is that since usually everything I do is with a GET request, I just put any key-value pairs in the URL and access the information server side. However, since the file data may (and probably will) exceed the <a href=\"http:\/\/stackoverflow.com\/questions\/417142\/what-is-the-maximum-length-of-a-url\/417184#417184\">URI size limit<\/a>, it&#8217;s best to go with POST.<\/p>\n<p>So then you may be asking &#8220;how do we get the files to go along with the XHR request?&#8221; It&#8217;s actually quite simple: just use a <a href=\"http:\/\/dev.w3.org\/2006\/webapi\/XMLHttpRequest-2\/#the-formdata-interface\"><strong>FormData<\/strong> object<\/a>.<\/p>\n<div class=\"codecolorer-container javascript railscasts\" style=\"overflow:auto;white-space:nowrap;width:100%;height:300px;\"><table cellspacing=\"0\" cellpadding=\"0\"><tbody><tr><td class=\"line-numbers\"><div>1<br \/>2<br \/>3<br \/>4<br \/>5<br \/>6<br \/>7<br \/>8<br \/>9<br \/>10<br \/>11<br \/>12<br \/><\/div><\/td><td><div class=\"javascript codecolorer\"><span class=\"kw1\">var<\/span> xhr <span class=\"sy0\">=<\/span> <span class=\"kw1\">new<\/span> XMLHttpRequest<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><br \/>\nxhr.<span class=\"me1\">upload<\/span>.<span class=\"me1\">addEventListener<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">'progress'<\/span><span class=\"sy0\">,<\/span><span class=\"kw1\">function<\/span><span class=\"br0\">&#40;<\/span>ev<span class=\"br0\">&#41;<\/span><span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; console.<span class=\"me1\">log<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#40;<\/span>ev.<span class=\"me1\">loaded<\/span><span class=\"sy0\">\/<\/span>ev.<span class=\"me1\">total<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">+<\/span><span class=\"st0\">'%'<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><br \/>\n<span class=\"br0\">&#125;<\/span><span class=\"sy0\">,<\/span> <span class=\"kw2\">false<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><br \/>\nxhr.<span class=\"me1\">onreadystatechange<\/span> <span class=\"sy0\">=<\/span> <span class=\"kw1\">function<\/span><span class=\"br0\">&#40;<\/span>ev<span class=\"br0\">&#41;<\/span><span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"co1\">\/\/ Blah blah blah, you know how to make AJAX requests<\/span><br \/>\n<span class=\"br0\">&#125;<\/span><span class=\"sy0\">;<\/span><br \/>\nxhr.<span class=\"me1\">open<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">'POST'<\/span><span class=\"sy0\">,<\/span> url<span class=\"sy0\">,<\/span> <span class=\"kw2\">true<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><br \/>\n<span class=\"kw1\">var<\/span> files <span class=\"sy0\">=<\/span> document.<span class=\"me1\">getElementById<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">'file-input-element'<\/span><span class=\"br0\">&#41;<\/span>.<span class=\"me1\">files<\/span><span class=\"sy0\">;<\/span><br \/>\n<span class=\"kw1\">var<\/span> data <span class=\"sy0\">=<\/span> <span class=\"kw1\">new<\/span> FormData<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><br \/>\n<span class=\"kw1\">for<\/span><span class=\"br0\">&#40;<\/span><span class=\"kw1\">var<\/span> i <span class=\"sy0\">=<\/span> <span class=\"nu0\">0<\/span><span class=\"sy0\">;<\/span> i <span class=\"sy0\">&lt;<\/span> files.<span class=\"me1\">length<\/span><span class=\"sy0\">;<\/span> i<span class=\"sy0\">++<\/span><span class=\"br0\">&#41;<\/span> data.<span class=\"me1\">append<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">'file'<\/span><span class=\"sy0\">+<\/span>i<span class=\"sy0\">,<\/span> files<span class=\"br0\">&#91;<\/span>i<span class=\"br0\">&#93;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><br \/>\nxhr.<span class=\"me1\">send<\/span><span class=\"br0\">&#40;<\/span>data<span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><\/div><\/td><\/tr><\/tbody><\/table><\/div>\n<p>You might be wondering why not just use the <a href=\"http:\/\/www.w3.org\/TR\/FileAPI\/#dfn-filereader\">FileReader interface<\/a> and upload with something pre-built like the <a href=\"http:\/\/api.jquery.com\/jQuery.ajax\/\">jQuery $.ajax()<\/a> method, and you&#8217;ll find the answer in the progress event listener; the $.ajax() method and the like doesn&#8217;t allow for it and I really want to display an upload progress bar, dammit!<\/p>\n<h2>Is that it?<\/h2>\n<p>Yeah, that&#8217;s it. Pretty easy, right? On the server side, the files are accessed just as if they were uploaded via a form and POSTed (in PHP&#8217;s case, they&#8217;re located all in the $_FILES array). I bundled it all up in a nice little JavaScript object which you can find over at <a href=\"https:\/\/github.com\/mockenoff\/HTML5-AJAX-File-Uploader\">my GitHub<\/a> complete with options and some helpful methods. Have fun with it and be sure to leave a comment if you think of something I could\/should change.<\/p>\n<p><a href=\"http:\/\/timothypoon.com\/blog\/demos\/ajax-file-api\/\" title=\"HTML5 AJAX File Uploader\">View the demo<\/a> (requires an HTML5-capable browser). I hacked together my own version of a &lt;progress&gt; element since at the time of this writing, only WebKit browsers have it sufficiently implemented (though I can&#8217;t seem to figure out why it doesn&#8217;t fully function as expected in Opera).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Prior to HTML5 and its File API, getting media from a user was a somewhat sordid affair. You generally either had a static page with an &lt;input&gt; element of a &lt;form&gt; posting to a processing file or utilized some third party script that relied on a Flash bridge or &lt;iframe&gt; shenanigans to asynchronously get the file where it needed to go. This leads to things like this demo and how you can drag &#8216;n drop (&#8216;n upload) files with Gmail. Seeing as how I was tired of doing the aforementioned&#8230;<a class=\"read-more\" href=\"https:\/\/timothypoon.com\/blog\/2011\/05\/10\/ajax-uploading-with-html5s-file-api\/\">read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[28,27,16,18],"class_list":["post-134","post","type-post","status-publish","format-standard","hentry","category-code","tag-ajax","tag-file-api","tag-html5","tag-javascript","et-no-image","et-bg-layout-dark","et-white-bg"],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p462yg-2a","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/timothypoon.com\/blog\/wp-json\/wp\/v2\/posts\/134","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/timothypoon.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/timothypoon.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/timothypoon.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/timothypoon.com\/blog\/wp-json\/wp\/v2\/comments?post=134"}],"version-history":[{"count":7,"href":"https:\/\/timothypoon.com\/blog\/wp-json\/wp\/v2\/posts\/134\/revisions"}],"predecessor-version":[{"id":301,"href":"https:\/\/timothypoon.com\/blog\/wp-json\/wp\/v2\/posts\/134\/revisions\/301"}],"wp:attachment":[{"href":"https:\/\/timothypoon.com\/blog\/wp-json\/wp\/v2\/media?parent=134"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/timothypoon.com\/blog\/wp-json\/wp\/v2\/categories?post=134"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/timothypoon.com\/blog\/wp-json\/wp\/v2\/tags?post=134"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}