---
title: Ridiculously simple Ajax uploads with FormData
teaser: 'Use the JavaScript FormData object to make Ajax based file uploads simple.

  '
tags: javascript,new bamboo,web
author: Pablo Brasero
published_on: 2012-01-10
---

_This post was originally published on the New Bamboo blog, before [New Bamboo
joined thoughtbot in London][new-bamboo-thoughtbot]._

---

<div class="updates-to-post">
  <dl>
    <dt>31/July/2014</dt>
    <dd>Added the <em>Android inconsistencies</em> section</dd>
  </dl>
</div>

## Introduction

Back in June 2010, I published a blog post detailing [how to perform Ajax file
uploads from your HTML forms][ajax-file-uploads]. The result worked pretty well,
but there was still some room for browser vendors to make things even simpler
for us.

Turns out, they have. The specific improvement that made this possible is the
[`FormData` interface][form-data], first introduced in Safari 5, and later in
Chrome 7 and Firefox 4.

## Compatibility and detection

As usual, this doesn't really work across all browsers in the market (you know
the usual suspects). Also as usual, the best way to know if the client supports
this feature is [object detection]. I have updated the function we used in 2010,
adding a check for the `FormData` interface:

```javascript
function supportAjaxUploadWithProgress() {
  return supportFileAPI() &amp;&amp; supportAjaxUploadProgressEvents() &amp;&amp; supportFormData();

  function supportFileAPI() {
    var fi = document.createElement('INPUT');
    fi.type = 'file';
    return 'files' in fi;
  };

  function supportAjaxUploadProgressEvents() {
    var xhr = new XMLHttpRequest();
    return !! (xhr &amp;&amp; ('upload' in xhr) &amp;&amp; ('onprogress' in xhr.upload));
  };

  function supportFormData() {
    return !! window.FormData;
  }
}
```

If the function returns true, you are good to go. If not, it may still be that
the old technique can still be used (although chances are slim). If neither is,
you will have to resort to some other way to do this, such as Flash (argh!) or
traditional form submission.

## Submitting the whole form

I won't dwell in details such as the events, as they haven't changed since the
[last time][ajax-file-uploads]. What has changed is what the
`XMLHttpRequest.send()` call receives. Last year, we fed it an instance of the
`File` interface. This time instead, we will give it an instance of `FormData`.

`FormData` gives us two ways to interface with it. The first and simplest is:
get a reference to the `form` element and pass it to the `FormData` constructor,
like so:

```javascript
var form = document.getElementById('form-id');
var formData = new FormData(form);
```

This new `FormData` instance is all you need to pass on the
`send()` call:

```javascript
var xhr = new XMLHttpRequest();
// Add any event handlers here...
xhr.open('POST', '/upload/path', true);
xhr.send(formData);
```

This will send an Ajax request with all the fields of the form on it, not only
file inputs. If there were also text areas, text fields, checkboxes or what have
you, they'll be sent too. Any events that you may be listening to will be
called, such as `onprogress` or `onreadystatechange`.

## Submitting only the file

The solution proposed above, albeit useful, is a bit limiting in that we are
forced to submit the whole form. Instead, we may want to submit the file
independently of the rest of the form. This is common nowadays in places such as
bulk photo uploaders on many social networks.

Fortunately `FormData` also allows us to do it this way. For this, it provides
the method `append(name, value)`:

```html
<!-- The HTML -->
<input id="the-file" name="file" type="file">
```

```javascript
// The Javascript
var fileInput = document.getElementById('the-file');
var file = fileInput.files[0];
var formData = new FormData();
formData.append('file', file);
```

And now the `formData` object is ready to be sent using `XMLHttpRequest.send()`
as in the previous example.

## Progressive enhancement

One immediate advantage over the old method: the data received on the server
side will be indistinguishable from a normal form submission. With the old
method, we had to add specific code on the server side to handle the incoming
data in a specialised way. This is not necessary anymore, and removed code is
debugged code!

There is another great advantage: it is easier to add progressive enhancement.
We can have a perfectly normal form, and add this Ajax on top. This way, if the
necessary JS interfaces aren't supported, the form will still work (although not
as nicely). It would be something like this:

```html
<!-- The HTML -->
<form id="the-form" action="/upload/path" enctype="multipart/form-data">
  <input name="file" type="file">
  <input type="submit" value="Upload" />
</form>
```

```javascript
// The Javascript
var form = document.getElementById('the-form');
form.onsubmit = function() {
  var formData = new FormData(form);

  formData.append('file', file);

  var xhr = new XMLHttpRequest();
  // Add any event handlers here...
  xhr.open('POST', form.getAttribute('action'), true);
  xhr.send(formData);

  return false; // To avoid actual submission of the form
}
```

In the example above, we send the XMLHttpRequest on submit of the form. We also
read the `action` attribute of the form to know where to send the request. Of
course, it's missing some event handlers that we'll use to update the interface.

The beauty of this example is: if the object detection script returns false,
this form will still work, and therefore functionality will remain intact.

## Android inconsistencies

But there's still one problem: some Android devices don't play entirely well
with this technique. They sort of work, but the **load** and **progress** events
won't fire. I have found this problem across versions of the OS and browser
apps. After all, remember that [there's no such thing as The Android
Browser][android-browser].

This is not the end of the world though. A way to mitigate this problem is to
make our webapp work so that it doesn't really matter much if those two events
are not fired. Obvious when you think of it:

1. Listen to the **loadstart** event and show some kind of pseudo-progress
  feedback. For example, an indeterminate progress bar or a spinner.
2. If and when **progress** is triggered, remove this pseudo-progress element
  and replace it with a real progress bar.
3. Don't rely on the **load** event to know when the upload has finished.
  Instead, use good old **readystatechange**. However, bear in mind there's a
  subtle difference in behaviour: **load** will trigger when the upload is
  finished, without waiting for the response from the server. On the other hand
  **readystatechange** will wait for a response from the server, with occurs
  later.

## A complete example

But enough of theory. I have updated the old example to use this new technique.
You can find [this updated example at GitHub][example]. There are comments at
every step of the client code, but don't hesitate to ask if anything is not
clear.

[ajax-file-uploads]: https://thoughtbot.com/blog/html5-powered-ajax-file-uploads
[android-browser]: http://slides.com/html5test/the-android-browser
[example]: https://github.com/newbamboo/example-ajax-upload
[form-data]: https://developer.mozilla.org/en/XMLHttpRequest/FormData
[new-bamboo-thoughtbot]: https://thoughtbot.com/blog/new-bamboo-joins-thoughtbot-in-london
[object detection]: http://www.quirksmode.org/js/support.html
