The File API has changed

Recently I have been touting how awesome and revolutionary the File API is through a few demo’s. After some feedback on webapps mailing list there have been some major changes to the API and how it works.

I have updated my previous drag and drop upload demo to reflect the API changes, as of Firefox 3.6b3 the API supports both the original API and the updated one. The older model will eventually be dropped, it’s only in there for legacy purposes. To use the demo you will need Firefox 3.6 installed. You can also watch the screencast of it in action.

The biggest change is with the file handling, it is now processed asynchronously with progress events so we can attach listeners. The advantage of this is if a user drags in many files or a large file the UI won’t be locked up while it’s processing the data, much like XmlHttpRequest works.

The File object

The File object has been updated to reflect the new specs changes and has deprecated all the previous methods we used to get the file in various formats e.g. getAsDataUrl(), getAsText(), getAsBinary(). We now handle these methods in the new FileReader object.

It has also renamed the 2 properties for accessing the files name and size from fileName/fileSize to name/size respectively.

The FileReader object

This new object allows us to asynchronously read the contents of a file from a drop event.

var reader = new FileReader();

reader.addEventListener("loadend", TCNDDU.buildImageListItem, false);
reader.readAsDataURL(file);

Instead of locking the UI while we wait for it to loop through all the dropped files and then convert them to a DataUrl. The FileReader does this asynchronously. We attach to the onloadend event handler which will fire once the current file has been read into the result attribute. Upon the event firing we then take the event result and add the DataURL to the source of the image to be displayed to the user while it uploads.

Send the binary data

Once we have the file we send it to be processed for an XHR call so we can upload it to the server. Same as above we need to create a file reader and attach to the onloadend event which we then pass to the sendAsBinary() method.

var getBinaryDataReader = new FileReader();

getBinaryDataReader.addEventListener("loadend", function(evt){xhr.sendAsBinary(evt.target.result);}, false);
getBinaryDataReader.readAsBinaryString(file);

Similar code we used for the image display handling, but we use the readAsBinaryString() method to return the files binary data for uploading.

Further reading

As the 3.6 final release date is coming closer Mozilla has been ramping up demos and documentation about the File API. Some good documentation on handling files in web applications.

Update: Mika Tuupola has a good article on handling the server side (PHP) part of file uploading.

The hacks blog has recently put up some great information about the File API along with an excellent demo extracting EXIF data from an image.

Now we just need webkit to push out the File API to their nightly builds.

Post filed under: html5, javascript.

Skip to comment form.

  1. Luke says:

    Any word on how this will play with Safari? I’m working on a site that only need to support FF and Safari and this could be a great addition.

  2. surreydude says:

    Nice work!

  3. Tested the demo to work in the release version of Firefox 3.6. Very cool. I noticed after the image is “uploaded” it outputs the image as a base64 encoded data URI… any reason you’re doing this instead of storing the image on the server?

    I assume it is possible to simply store the image on the server if desired.

    • @Michael – It’s actually encoded into a base64 string before it uploads (using the File API). That way I can display the image to the user before any uploading happens. If you disable your network connection and drag in the images it will still display them but will obviously fail on trying to upload them to the server.

  4. Michael says:

    That makes sense! I can see how easy it is then to use the offline data API to store the images locally and upload them later on. Can’t wait to try this out.

  5. When I try the demo in FF3.6 on Ubuntu, Firefox tries to open the dropepd document, instead of uploading them. It seems to ignore the File and Drag And Drop APIs.

    • @Émile – That’s not good, I don’t have a linux machine available for me to test on so I can’t see what’s happening. Can you try my font dragr web app (it uses the file API too) in FF3.6 on your linux machine and let me know if you have the same issue?

  6. The font dragr doesn’t work either. It seems to be exactly the same issue.

    • @Émile – So I asked around for a few people with linux to try my demo and it’s working for them, but someone did mention that having the Jetpack add-on installed kills the drag and drop functionality but only on linux machines, mac/windows are OK. It’s been filed in the issue tracker so they’re aware of it.

  7. I disabled Jetpack, and now it works all right! Thank you!

  8. Shawn says:

    This is really cool! Ubuntu/Firefox isn’t working for me (no Jetpack here), but it works great on my windows machine. I’m going to hack on it for a while to see if I can figure out where Linux is going wrong…

    • Shawn, don’t have a linux box to test on so I can’t help you on that front. Only linux issue is the Jetpack plugin but you’ve stated you don’t have it. If you figure out the problem I would be interested to know what it was.

  9. Curt says:

    Could you update your article to include a little blurb on how update.php handles it? As in: $_POST[‘what goes here???’]. I don’t see a name property set anywhere for the post data in your code…

  10. @Curt
    Using some inspiration from this article, I came up with a JSON solution to upload both binary and/or user data.
    http://alex-tech-adventures.com/development/x-html–css–javascript/97-drag-and-drop-upload-using-html5-with-firefox.html

  11. Guillaume says:

    hi,
    i’m trying to get the server site working :

    my upload.php server side is a simple var_dump($_FILES)

    i added an event listener for the ready state change for the xhr to log the response

    i get an empty array

    firefox 3.6
    php 5.2.10
    apache 2.2.11
    on windows XP 32b

  12. Holger says:

    Looks nice, but how to store the files on the server?

    I’m sorry if I read over this – English is not my first language..

    The article you postet on April 24th doesn’t want to help me ;)

  13. any chance you could let me know how to add “cancel (single object)” and/or “cancel all” method(s)?

  14. Clark says:

    Thanks for the sample, really helped me a lot!

    However, I discovered a small bug in the handleDrop() function which prevents the file name from being propagated past the drop event. Not really a big deal, but for what I’m doing some information is encoded in the file name which I need to access between the drop and the upload. Anyways, to fix the issue line 96 should be setting reader.name to droppedFileName.

  15. salvador says:

    know how I can get the data in a JSP instead of php,because I need to upload the files using JAVA

  16. Weixi Yen says:

    I just finished a jQuery plugin for this:
    http://github.com/weixiyen/jquery-filedrop

    Assuming Webkit follows suit, this should be future-compatible, but it works perfectly with FF3.6 right now.

  17. Farzan mc says:

    WoooWW
    Best thing ever in html
    I saw this in gmail and later on in google wave presentation, i didn’t think that i can find the source code …..
    Thanks a lot, it will sure come in handy in my projects.
    Wooooooooooow :D :D

  18. I was implementing some of these features by looking in your zip file download… ran in to an issue where you do:
    fileUpload.log = container;

    But the log property doesn’t carry over 90% of the time in the load event of the XHR… you must have realized this because this part is commented out in your live demo, which uses anonymous functions to keep a handle on the progress bar!

  19. Adam says:

    One of the problems I keep coming up against with drag & drop is the lack of affordance. Even in gmail– I don’t know that I can drag and drop an image until I’ve already tried it, and the actual area I can drop my files in isn’t the “Rich Text” portion of the app, which is debatably where I’d want to see my file.

    One of the things I’ve tried is to simply swap classes on input, so that if the user drags a file into the window, the entire window can receive the file. Otherwise, style it to look like a button in my rich text editor so that it behaves normally.

    After successful drop, a small “Uploading” message is set with a fixed position, similar to gmail’s “sending…” message.

    Upon successful upload, the final file location(s) (I’m resizing along the way) are sent back as jsonp so that img tags can be generated and inserted into the rich text editor.

    Not feasible for say, a CMS with multiple Rich Text fields, but less does indeed seem to be more when it comes to managing user expectation of how dropping files should operate.

  20. Adam says:

    I should note it really doesn’t solve the affordance problem, it just seems to be what laypeople expect from my limited experience testing :]

  21. hey CSS Ninja, thought you might want to know that it seems Chrome version 6 now has the FileReader() class, but it might be incomplete because it says “Object #<a> has no method ‘addEventListener'”. any ideas why you can’t use addEventListener methods on FileReader objects?

    • @Michael B – Yep I filed a report on their bug tracker a while back, it’s still in their latest canary build also which is v7. However there is an easy work around, use DOM0 event listeners e.g. reader.onloadend = f(){} I did make the changes to my font drag web app so that will work in Chrome 6.

  22. Joe says:

    Ninja, thanks for this tutorial. I’m using Firefox 3.6.10 and when I look at the response in firebug from the post/upload, I just see:
    Array
    (
    )

    I see that the file looks like it was posted correctly – is that response to be expected, or should there be content in that array? I’m seeing this both on your demo and on my test on my local machine. Thanks!

  23. Joe says:

    Perfect – that did the trick. Obviously, I missed that comment the first time though, so thanks for pointing it out!!

  24. samuel elrod says:

    The code to POST to the upload.PHP does not appear to be correct as $_FILES is null with posted data.

  25. @Samuel it’s very simple and has already been discussed. using the CSS Ninja style ajax upload, there will be no $_FILES array! On PHP, simply do $file_data = file_get_contents(‘php://input’); and you have the file data right there.

  26. sticky says:

    Wait where can you define where it is uploading the files? I have tried to use your script and it appears it’s working but doesn’t appear to upload the images anywhere on the server?

  27. Woj says:

    Maybe you will be interested with new JS library, which can be used to add drag&drop upload files functionality which works on Firefox, Safari and Chrome: http://code.google.com/p/html5uploader/ It’s on one of open source licenses.

  28. Robert says:

    Hey ninja Great Work here. Thank You.
    This is exactly what I need for an intranet app I am working on. However, I wanted to add the functionality of being able to keep adding pictures to the container. Right now if you drag for example 5 pictures, it previews all 5 pictures. And if you drag another 2 it previews only the last 2 I dragged. I wanted it so it keeps on adding them to the list, and also so that they could erase from it. I think that’s the only thing missing.
    Plus I am not uploading the pictures to the server, since all I want is the actual path of the pictures. (that’s taken care of)

    The example here let’s you keep adding pictures without removing the old ones: http://code.google.com/p/html5uploader/
    I looked thru both codes but I can’t figure out what to change in yours to make it work.

    Can you please guide me on how to do this.

    Thanks in advance.

    • @Robert – That is an easy change. In the TCNDDU.setup() method I attach the dragenter event and in there I clear the drop area using dropListing.innerHTML = '';, just remove that line and images won’t be removed.

  29. Roberto says:

    Hey Ninja! Thanks for this great work, very neat!

    I have a question as to if it would be possible to pre-populate the dropContainer from file paths?

    I have an edit page where users should be able to see the pictures already loaded in the dropContainer.

    Is that possible or is that a security threat? I do have the correct file paths tho.

  30. Roberto says:

    Thanks for the quick response. I found a workaround the security issue by creating a virtual directory in my intranet mapping to the shared folder where all the images are located. So now I am able to get full URL paths to the images.
    But I still can’t figure out a way to pre-populate the images in the container.
    How should I go about doing this?

    • @Roberto – You can’t iterate over a file system even if you have the full URL path to the images. You would need some server-side code to let you know what files are in the images folder. Do an XHR get request to a webservice that scans the folder returns all the files in JSON. Iterate over the image names, create an img element and append path plus filename, lastly append the documentFragment to the document itself.

  31. Jmactacular says:

    This is amazing. Have you had a chance to try and get this working in Chrome or Safari since they support HTML5?

    • @Jmactacular – Chrome since version 6 has had support for the File API, only issue is that the FileReader object doesn’t support DOM2 event listeners so you’ll need to use DOM0 to get it working on chrome.

  32. Bob says:

    Hi ninja! Great work.

    How would I go about uploading the files to a ftp server. I don’t know how to get the $_FILES[] global array since one doesn’t exist since you don’t use one, or do you?

    Well I need to upload the files to a ftp server using php, I need to be able to get the all the files from the $_FILES[] array once my form is submitted but I can’t see how to it with your implementation of drag and drop. Is it possible?

    Thank you

  33. luokai says:

    please give me a Demo code ,this code isn’t upload.php file

  34. droid says:

    Brilliant! thanks for the demo.
    File opens in IE and Chrome but works a treat in FF
    Thanks for all the links posts too will give it all a good read… then drop files directly to database through PHP.
    Perfect for my project with special needs kids THANKS

  35. Dipak Basantani says:

    Hi Ninja,

    its really very cool functionalityand very good. I have implemented
    in one of my project.

    Still I got one problem.

    is there any limitation on the size of bytes on total files uploaded. If i upload total 7 files . each files are of more then 1 Mb size and seems like it does not upload the 7th file.

    and getBinaryDataReader.readAsBinaryString(file); gives error on upload. its exception error. so flow does not interrupted but the file was not uploaded.

    please suggest on this if you got any idea.

    Thanks

  36. Sarvesh says:

    Hello There,
    Thanks for such a nice plug-in, this is what I am looking for, This plugin is working fine on firefox,but it does not working on other browsers, Could u please focus on the same, so that user can enjoy the plugin for other browser.


    Thanks & regrads
    Sarvesh

  37. Ryan: It works fine. However, the upload.php script does NOT see any $_POST variables. The script gets called, but the $_POST array is empty. Using production 3.6 browser.

    Thoughts?

    Larry

  38. manikavasakam says:

    how I can get the data in a JSP instead of php,any comments

  39. Enrico says:

    Thank you very much for your the effort into this code, but please let me say that it is quite useless without a working upload.php file. I read the links you provided about and tried long to work it out, but without success. Many of us are not so proficiency in PHP to change the code to fit it into your example. So, meanwhile some guru publishes here a working upload.php, none of us will fully enjoy your code in our applications.

  40. pilus says:

    tested the live demo on chrome 12.0.742.112/Win, there’s an error popped out on the devtool’s console, it says:

    Object # has no method ‘addEventListener’
    on: /demo/drag-drop_upload/v2/:114

    I think the latest File API don’t have addEventListener or something, but i’ve successfully implemented the same functionality using:

    FileReader.onloadend = function(){};

    and it works on chrome 12, FF4/5, and opera 11

    the only problem that I have is with readAsBinaryString on file larger than 100KB, the onloadend seems to never called, even after waiting for 10 minutes (yupz, i actually waited for that long …. @__@)

    as you might already know, i use readAsBinaryString for uploading to the server … but i never got a chance to upload anything, as everytime i try to readAsBinaryString it never ends … >.<

    would really appreciate some help, tried googling this, but doesn't seem to be anything to help me …

    in the meantime will try to slice the file for readAsBinaryString, it that's possible at all …

    cheers

    • Ryan Seddon says:

      @pilus

      Yes I too came across DOM2 event handling not working on FileReader, I filed a bug a long time ago but it still hasn’t been fixed.

      Haven’t come across your issue with readAsBinaryString not working. Do you have a testcase for this?

  41. Praveen says:

    I went through all the above post but your are redirecting to http://www.appelsiini.net/2009/10/html5-drag-and-drop-multiple-file-upload when ever some one asks for the server side script.

    I tried many ways with $_POST, $_FILES and then finally file_get_contents(“php://input”). $_POST and file_get_contents(“php://input”) are returning some data which is not giveing proper result when sent to image tag in PHP as below
    $file_data = file_get_contents(“php://input”);
    print “”;

    This is not useful for me until I store the posted data into server.

    If anyone have got success with PHP. Please paste the code in this thread.

    Thanks

  42. Hi Ryan,

    Thanks so much for providing this demo. The code reads really well and is a great starting point I feel for many developers (including me) getting their head around the Drag and Drop capabilities.

    Keep up the great work.

  43. Similar to your code i’ve came up with this
    http://www.ashantyk-design.com/dev-3-Upload%27em+JS

    I said similar because i pulled out the % loading thing because that shows the progress of the file being loaded into the browser, not the server and i put in a connection limit part with queueing.

    Also i extended the delete/cancel function. users can delete uploaded files after upload completion, using a session variable so they won’t delete any files that they didn’t uploaded (security reasons)

  44. Nenad says:

    Hi Ninja!

    Great work! I was inspired by your drag and drop uploader so I rewrote it and optimized it to work also in chrome browser.

    The demo is on:

    http://www.ninja-scripts.com/demos/drag/

    and the uploaded files:

    http://www.ninja-scripts.com/demos/drag/files

    Regards,
    Nenad

  45. zero says:

    Can the demo work?

    Uncaught TypeError: Object # has no method ‘addEventListener’