interactive file uploads with Drag and Drop, FileAPI and XMLHttpRequest

In previous posts, we showed how to access a file through the input tag or through the Drag and Drop mechanism. In both cases, you can use XMLHttpRequest to upload the files and follow the upload progress.

Demo

If you’re running the latest beta of Firefox 3.6, check out our file upload demo.

Uploading

XMLHttpRequest will send a given file to the server as a binary array, so you first need to read the content of the file as a binary string, using the File API. Because both Drag and Drop and the input tag allow you to handle multiple files at once, you’ll need to create as many requests as there are files.

var reader = new FileReader();
reader.onload = function(e) {
  var bin = e.target.result;
  // bin is the binaryString
};
reader.readAsBinaryString(file);

Once the file is read, send it to the server with XMLHttpRequest:

var xhr = new XMLHttpRequest();
xhr.open("POST", "upload.php");
xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
xhr.sendAsBinary(bin);

You can choose to be notified when specific events, such as error, success, or abort, occur during the request (see the MDC documentation for more details).

Following the Upload Progress

The progress event provides the size of the uploaded portion of the binary content. This allows you to easily compute how much of the file has been uploaded.

Here’s an example showing the percentage of the file that has been uploaded so far:

xhr.upload.addEventListener("progress", function(e) {
  if (e.lengthComputable) {
    var percentage = Math.round((e.loaded * 100) / e.total);
    // do something
}, false);

About Paul Rouget

Paul is a Firefox developer.

More articles by Paul Rouget…


27 comments

  1. Michael Irwin

    Wow! This is looking really good. I can’t wait for the final release to come! :-)

    December 15th, 2009 at 11:40

  2. Ryan

    I like the canvas loader, sexy.

    A few observations from my own drag and drop uploader I did a while back, http://www.thecssninja.com/javascript/fileapi

    – Using a function passed into the progress event listener and using e.target to access the loader container fails randomly and loses reference to itself. To fix this I used an anonymous function inside the listener rather than passing on the duties to external function. That way I could directly access the container rather than using e.target.

    – Another issue is if I drag in the Windows Vista sample images they all begin to upload fine, but when it gets to 2 particular files it craps itself and throws an exception

    uncaught exception: [Exception… “Component returned failure code: 0x80004003 (NS_ERROR_INVALID_POINTER) [nsIDOMFileReader.readAsBinaryString]” nsresult: “0x80004003 (NS_ERROR_INVALID_POINTER)” location: “JS frame :: http://thecssninja.com/demo/drag-drop_upload/v2/ :: anonymous :: line 147″ data: no]

    2 files always fail and are the 2 largest files out of the sample images. There seems to be sporadic failures with images larger than ~550kb, but this also depends on how many images I’m uploading.

    December 15th, 2009 at 23:14

    1. Mark S

      Ryan, have you got a chance to get around Component returned failure code: 0x80004003 (NS_ERROR_INVALID_POINTER) [nsIDOMFileReader.readAsBinaryString]” nsresult: “0×80004003 (NS_ERROR_INVALID_POINTER)” location: problem?

      I’m going to try /catch it but it’s a pity it’s almost been an year since that comment and no one seems to fix that bug.

      October 28th, 2010 at 12:37

      1. Ryan

        Mark, I haven’t really looked into it for a while. A potential solution, although only in FF4, would be to use the createBlobURL[1] method so you can link to the file and display it without loading it all into memory. Loading to much data directly into memory could potentially be the cause of the exception. Paul if you have a better insight please let us know.

        [1] https://developer.mozilla.org/en/DOM/window.createBlobURL

        October 28th, 2010 at 15:29

        1. Mark S

          FF4 is not going to cut it -)
          Trying to check the status of those and work around to retry uploading automatically.
          Thanks a lot, great work on your site, let me know if you find anything.

          October 28th, 2010 at 15:50

  3. Jose

    Does anyone know if downloading files in the same way is possible? Or is that something that might come in a future release (if at all)? Would this bug https://bugzilla.mozilla.org/show_bug.cgi?id=338478 be that?

    December 16th, 2009 at 03:29

  4. thinsoldier

    In a future post you should show how to take a dropped 5 megapixel photo and use canvas to resize it down to both an 800×600 and a 100×75 thumbnail. Then upload those 2 canvas generated images to the server instead of the huge original.

    December 17th, 2009 at 09:12

  5. iNsuRRecTiON

    Hi there,

    nice, but you are unable to remove single pictures from the drop zone, if you don’t want to send this pic.

    Example, you are select 15 pics, but then you see in the preview in the drop zone, you don’t want to send two of them.
    Now there is no ability to remove the two pics.
    You have to re drag and drop or open dialog the files, without those two, which is bad user experience!

    regards,

    iNsuRRecTiON from Germany

    December 18th, 2009 at 13:35

  6. Chrom

    @insurrection
    That could be easily solved by adding a “waste-basket” that you can drag added files to.

    December 29th, 2009 at 04:48

  7. Christopher

    Am I right that all files are transferred through the browsers memory since they are available as binary string within the JavaScript?
    I suppose that if I read (or upload) a 100 MB file via FileReader, my browsers memory usage increases accordingly?!

    Is that intended?

    December 29th, 2009 at 12:53

  8. Tom

    +1 for thinsoldier’s suggestion.

    I’d love to see resizing before upload example. This can be achieved with Gears at the moment (e.g. Google Wave). But I’d love to see how to do it with File API.

    Thanks for this great work.

    January 21st, 2010 at 01:59

  9. andym801

    thinsoldier’s suggestion would be nice, but you guys are over-complicating the simplicity of a Drag and Drop upload system. Maybe in the future they’ll showcase a Drag and Drop photo re-sizer.

    What I would like to see… is a green check-mark or red “x” on my photos to confirm whether or not the upload was successful. This would be handy if you had a lot of big photos to upload, and wanted to walk away from your computer. In which case you wouldn’t have seen the little progress ring.

    January 21st, 2010 at 19:40

  10. […] And according to the new specs. This would allow for easy file uploads. Now there’s been some examples [2] on the web already. But i just wanted to get my hands […]

    January 24th, 2010 at 16:02

  11. […] of the API, showing first multiple file uploads, then a drag and drop upload interface, next adding progress information (although this doesn’t work for me), then reading EXIF data from a JPEG image. You can imagine […]

    January 25th, 2010 at 16:37

  12. […] uploads http://hacks.mozilla.org/2009/12/uploading-files-with-xmlhttprequest/ http://demos.hacks.mozilla.org/openweb/uploadingFiles/ (very […]

    January 26th, 2010 at 04:32

  13. Preben Borch

    Are there any server side PHP script examples for receiving the files. The form method works as expected, but when I try the XMLHttpRequest method I get nothing into the PHP script? Something I missed?

    January 28th, 2010 at 08:22

  14. Ryan

    Thia article delves into handling file uploads from a drag and drop action. http://www.appelsiini.net/2009/10/html5-drag-and-drop-multiple-file-upload

    January 28th, 2010 at 16:22

  15. Preben Borch

    I dumped the standard input to a file, and there it was. It would be a good idea with a more complete example – sending filename in the url would be a nice. Otherwise this new functionality is great! I have made a Google Gears upload which worked very well, but somehow updates of Google Gears is slow for new releases. Sending files in chunks would be a good idea as well, and publishing the server side script (well, I’m happy right now, but there may be others).

    January 29th, 2010 at 08:22

  16. Ryan

    Sending files in chunks is currently being disucssed on the webapps mailing list and therefore mozilla held off on adding this functionality to ff3.6. They will have a blob which can be sliced into smaller chunks, obviously gaining these ideas from google gears. http://www.w3.org/TR/FileAPI/#dfn-Blob

    January 29th, 2010 at 18:33

  17. Eric Jain

    Too bad you can’t set the dropped file(s) into an

    February 4th, 2010 at 17:48

  18. Craig Morrison

    I’ve got my take on this at:

    http://www.sillywindows.com/

    It is clearly obvious which link to click on to grab it.

    March 1st, 2010 at 21:55

  19. […] After you have read out the necessary data about the file(s), you can get the binary data by using a FileReader object and then use the XMLHTTPRequest object to post it to a web server. More information about that can be found in the FileReder documentation and in Paul Rouget’s article Interactive file uploads with Drag and Drop, FileAPI and XMLHttpRequest. […]

    April 22nd, 2010 at 08:32

  20. Daniel Kirsch

    How can I send both usual form data together with the file and use the progress indicator? Do I need to first upload the files using xhr.sendAsBinary and than send the other form data on success with a second request?
    In FF4 I could use FormData, however I also want to support FF 3.6.

    September 21st, 2010 at 15:02

  21. Helio Bentes

    How can I get the image and save it in my server using PHP?

    December 29th, 2010 at 06:26

    1. hash

      You may compose multipart request, send it via sendAsBinary method and deal with it at server side as you usually do width file uploadings.

      December 29th, 2010 at 14:56

  22. kris

    I’ve been playing around with the fileAPI a bit today – something has me stumped, and this may be a bit of a noob question, or a need-more-coffee question, but i’m going to ask it:

    what does the upload.php look like in this example??
    what does my server-side code look like to access the data i’m receiving ??

    is is all just raw data in $_POST ??? – i guess i can try that for myself and see can’t i … just a minute … yeah, no, $_POST is an empty array, so is $_FILES

    please help ! (I see that “Hello Bentes” and “Preben Borch” have asked the same question but no replies to this yet… “Ryan” your reply was great, but still wonder if there is a possible server-side program that doesn’t require creating a custom JS upload)

    February 24th, 2011 at 00:24

  23. Preben Borch

    kris, you need to open stdin in PHP and dump this to a file.

    To open a file pointer to stdin follow this example:

    http://php.net/manual/en/features.commandline.io-streams.php

    February 28th, 2011 at 10:24

Comments are closed for this article.