Mozilla

Firefox 4 – FormData and the new File.url object

This is a guest post from Jonas Sicking, who does much of the work inside of Gecko on content facing features. He covers FormData, which we’ve talked about before, but shows how it can connect to an important part of the File API we’ve added for Firefox 4: File.url.

In Firefox 4 we’re continuing to add support for easier and better file handling. Two features that are available in Firefox 4 Beta 1 are File.url and FormData. In this post I’ll give a short introduction to both of them.

Starting with Firefox 3.6 we supported a standardized way of reading files using the FileReader object. This object allowed you to read the contents of a file into memory to analyze its content or display the contents to the user. For example to display a preview of an image to a user, you could use the following script

var reader = new FileReader();
reader.onload = function() {
  previewImage.src = reader.result;
}
reader.readAsDataURL(myFile);

There are two unfortunate things to note here. First of all, reader.result is a data url which contains the whole contents of the file. I.e. the full file contents is kept in memory. Not only that, data urls are often base64 encoded, and each base64 encoded character is stored in a javascript character, which generally uses 2 bytes of memory. The result is that if the above code is used to read a 10MB image file, reader.result is a 26.7MB large string.

The other unfortunate thing is that the above code is somewhat complicated since it needs to use asynchronous events to read from disk.

In Firefox 4 Beta 1 you can instead use the following code

previewImage.src = myFile.url;

This uses the File.url property defined by the File API specification. The property returns a short, about 40 characters, url. The contents of this url you generally won’t have to care about, but for the interested it contains a randomly generated identifier prefixed by a special scheme.

This can url can then be used anywhere where generic urls are used, and reading from that url directly reads from the file. The example above makes the image element read directly from the file and display the resulting image to the user. The load works just like when loading from a http url, normal ‘load’ events and ‘error’ events are fired as appropriate.

You can also display HTML files by using an <iframe> and setting its src to the value returned by File.url. However you have to watch out for that relative url in the HTML file won’t work as the relative urls are resolved against the generated url returned from File.url. This is intentional as the user might have only granted access to the HTML file, and not to the image files.

Other places where this URL can be useful is for CSS background images, to set the background of an element to use a local file. Or even read from the url using XMLHttpRequest if you have existing code that uses XMLHttpRequest and which you don’t want to convert to use FileReader.

The other feature that we are supporting in Firefox 4 Beta 1 is the FormData object. This object is useful if you have existing server infrastructure for receiving files which uses multipart/form-data encoding.

In Firefox 3.6, sending a file to a server using multipart/form-data encoding using XMLHttpRequest required a a bit of manual work. You had to use a FileReader to read the contents of the file into memory, then manually multipart/form-data encode it, and then finally send it to a server. This both required more code, as well as required that the whole file contents was read into memory.

In Firefox 4, you’ll be able to use the FormData object from the XMLHttpRequest Level 2 specification. This allows the following clean code

var fd = new FormData();
fd.append("fileField", myFile);
var xhr = new XMLHttpRequest();
xhr.open("POST", "file_handler.php");
xhr.send(fd);

This will automatically multipart/form-data encode the file and send it to the server. The contents of the file is read in small chunks and thus doesn’t use any significant amounts of memory. It will send the same contents as a form with the following markup:

<form enctype="multipart/form-data" method="post">
  <input type="file" name="fileField">
</form>

If you want to send multiple files, simply call fd.append for each file you want to submit and the files will all be sent in a single request. You can of course still use the normal progress events, both for upload and download progress, that XMLHttpRequest always supplies.

However FormData also has another nice feature. You can also send normal, non-file, multipart/form-data values. For example

var fd = new FormData();
fd.append("author", "Jonas Sicking");
fd.append("name", "New File APIs");
fd.append("attachment1", file1);
fd.append("attachment2", file2);
var xhr = new XMLHttpRequest();
xhr.open("POST", "file_handler.php");
xhr.send(fd);

You can even get a FormData object which contains all the information from a <form>. (However note that the syntax for this is likely to change before final release)

var fd = myFormElement.getFormData();
var xhr = new XMLHttpRequest();
xhr.open("POST", "file_handler.php");
xhr.send(fd);

Here fd will contain data from all the form fields, from radio buttons to file fields, that are contained in the form.

As always, we’re all ears for feedback about these features. Please let us know what you think, and especially if you have tested them out and they do not appear to do what you expect them to. You can use http://www.mozilla.com/en-US/firefox/beta/feedback/ to give us feedback, or use the feedback button in the upper right corner (see below screenshot).

20 comments

Comments are now closed.

  1. David wrote on July 8th, 2010 at 09:56:

    Trying to set the source of an image using Firefox 4 Beta 1, jQuery, and File.url spec. The File.url property is returning a string (moz-filedata:xxxxx…), but when set as the src for the image, nothing comes back.

    Any ideas? Thanks.

    1. Jonas Sicking wrote on July 12th, 2010 at 02:00:

      Would love to see a testcase. One thing to keep in mind is that we’re unusually strict about same-origin checks. So a page in domain A can’t use the File.url value produced by a page in domain B.

      Are you using document.domain or postMessage to transport the fileurl between pages of different domains by any chance?

      If the above isn’t a problem, file a bug in bugzilla or send me a testcase (should be easy to find an email address through google or other search).

    2. Paul Rouget wrote on July 15th, 2010 at 21:28:

      Could be this bug: https://bugzilla.mozilla.org/show_bug.cgi?id=572139

  2. srigi wrote on July 13th, 2010 at 09:18:

    Can you publish some infoooz about

    “read the contents of the file into memory, then manually multipart/form-data encode it and then finally send it to a server”

    I want to code some workaroud for FF v3.6 for Ajax file uploads. Since v3.6 doesn’t support FormData, I need implement this “hack”.

  3. Anshuman wrote on July 15th, 2010 at 00:28:

    can you please provide the info for “manual multipart/form-data encode” in FF3.6 …. i am using this code but its not working..


    var data = event.dataTransfer;

    var boundary = '------multipartformboundary' + (new Date).getTime();
    var dashdash = '--';
    var crlf = '\r\n';

    /* Build RFC2388 string. */
    var builder = '';

    builder += dashdash;
    builder += boundary;
    builder += crlf;

    var xhr = new XMLHttpRequest();

    /* For each dropped file. */
    for (var i = 0; i < data.files.length; i++) {
    var file = data.files[i];

    /* Generate headers. */
    builder += 'Content-Disposition: form-data; name="user_file[]"';
    if (file.fileName) {
    builder += '; filename="' + file.fileName + '"';
    }
    builder += crlf;

    builder += 'Content-Type: application/octet-stream';
    builder += crlf;
    builder += crlf;

    /* Append binary data. */
    builder += file.getAsBinary();
    builder += crlf;

    /* Write boundary. */
    builder += dashdash;
    builder += boundary;
    builder += crlf;
    }

    /* Mark end of the request. */
    builder += dashdash;
    builder += boundary;
    builder += dashdash;
    builder += crlf;
    try{
    xhr.open("POST", "fu.php", true);
    xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' + boundary);
    xhr.sendAsBinary(builder);
    return "Uploaded";
    } catch(e) {
    return e;
    }

  4. tobiistgut wrote on July 16th, 2010 at 15:20:

    How long will the URL provided by File.url work?

    1. Jonas Sicking wrote on July 24th, 2010 at 21:14:

      It will work until the user navigates away from the page.

  5. Firenze wrote on July 25th, 2010 at 18:17:

    Using this XMLHttpRequest Level 2 form method will open all gates to hell,
    malware writters will have a playground they’ve never seen before.

    Why don’t you think before you brew such crap code ?

    1. Paul Rouget wrote on July 26th, 2010 at 01:41:

      Need more details here. What do you mean?

  6. Werner Punz wrote on August 1st, 2010 at 08:29:

    Nice stuff, but so far the FormData object always enforces a multipart xhr submit, wouldn´t it be better to have the object as well for normal non multipart submit cases, it would ease a lot of things to expose the FormData object to all possible submit cases instead of just having a helper for multipart submits.

  7. Werner Punz wrote on August 1st, 2010 at 13:03:

    Ok I gave this stuff a try, unfortunately (Firefox 4 Beta2) it still is buggy, while it is sending the form data as multipart request, it lacks some definitions most multipart decoders rely upon. The send generates the multipart request without divider definitions and without length definition.
    Setting both manually also is not possible due to not being able to access the length of the generated submit content, and also due to the divider being generated at send time as well.

    This seems like a bug to me, any idea where I can report this to?

  8. Kensaku KOMATSU wrote on August 24th, 2010 at 23:39:

    Thank you for supporting FormData!! It’s make sense to me:)

    However, under the condition that uploading files with FormData method to Cross-Origin Server, I found that preflight sequence occurred w/ FireFox4 beta (I tried beta3 and 4). In the CORS spec, if the method is “POST” and content-type is “multipart/form-data”, preflight sequence should not be occurred. So, I think this behavior does not corresponds to CORS spec.

    P.S. Chrome 6 beta also supports FormData feature, and preflight sequence does not work with it.

  9. mawrya wrote on August 29th, 2010 at 20:49:

    Question: what if I want to add a web-accessible file to a form automatically via javascript? This example and others like it only cover user selected files – either via a form control or drag-and-drop. That makes sense for files on a user’s computer as there is a security concern. What if I want to add files that are already and ONLY accessible from a web address? There is no way to do this with a drag and drop interface, nor a file picker interface. I want a script that gets the file at http://localhost/blah.xml and includes it with the other user selected files in the form. I would like to use the FormData.append() method to get the file into the form as it takes care of all the messy multipart/form-data encoding but it seems I need to first get the blah.xml file into a FileAPI file object. There doesn’t appear to be any way to do this. Am I forced write javascript to manually read and multipart/form-data encode it and then attach it to the form somehow?

    And thanks for the great posts!

  10. Shiv Kumar wrote on September 19th, 2010 at 22:08:

    In your example:
    var fd = new FormData();
    fd.append(“fileField”, myFile);
    var xhr = new XMLHttpRequest();
    xhr.open(“POST”, “file_handler.php”);
    xhr.send(fd);

    what is myFile?
    I’ve tried document.getElementById(“someformelement”);

    where someformelement is a

    But it is not send the file over to the server. It sends the additional form fields if appended to the FormData.

    Sending the whole form using the next example works as expected.

  11. Shiv Kumar wrote on September 22nd, 2010 at 23:09:

    Answering my own question…

    If you have a form field with an id of \fileToUpload\ and this field is an input element of type file. then

    var myFile = document.getElementById(‘fileToUpload’).Files[0];

  12. Michel wrote on October 27th, 2010 at 09:17:

    Can Anyone help me correctly do a preflight cors post in Fx 3.6 ?
    http://stackoverflow.com/questions/4034223/correct-way-to-do-cors-preflight-post-using-jquery
    and will the same code work in Fx4?

  13. Sol wrote on November 22nd, 2010 at 01:02:

    How exactly File.url work? I get undefined trying to get it.

  14. Radek Tetík wrote on February 15th, 2011 at 06:04:

    FormData is a great API. I have just implemented it and works well in FF4b11 and Chrome 10. Sadly it does not work in IE9.

  15. CopyKat wrote on March 16th, 2012 at 04:02:

    Thank you for making sense of FormData, I have always found this so confusing. You were clear and articulate, and helped a me, who isn’t so techy.

  16. Stephanie Manley wrote on March 28th, 2012 at 03:56:

    It is very important information.
    Thank you very much

Comments are closed for this article.