Is it possible to perform an asynchronous cross-domain file-upload?

It is possible! Read below.


First of all, let me use this diagram to explain how asynchronous file uploads can be achieved:


Sorry. I've shut down one of my domains, and the image is gone now. It was a really nice image though. This was before I found out that Stack Overflow enables uploading images via Imgur.


As you can see, the trick is to let the HTTP-response load into a hidden IFRAME element instead of the page itself. (This is done by setting the target property of the FORM element when submitting the FORM with JavaScript.)

This works. However, the problem I'm facing is that the server-side script is on a different domain. The FORM-submit is a cross-domain HTTP-request. Now, the server-side script has CORS enabled which gives my web-page the rights to read the response-data of HTTP-requests made from my page to that script - but that works only if I receive the HTTP-response via Ajax, ergo, JavaScript.

However, int this case, the response is directed towards the IFRAME element. And once the XML response lands into the IFRAME, its URL will be the remove script - e.g. http://remote-domain.com/script.pl.

Unfortunately, CORS does not cover this case (at least I think) - I am not able to read the contents of the IFRAME since its URL doesn't match the URL of the page (different domain). I get this error:

Unsafe JavaScript attempt to access frame with URL hxxp://remote-domain.com/script.pl from frame with URL hxxp://my-domain.com/outer.html. Domains, protocols and ports must match.

And since the contents of the IFRAME is an XML document, there is no JavaScript code inside the IFRAME which could make use of postMessage or something.

So my question is: How can I get the XML contents from the IFRAME?

As I said above, I am able to retrieve cross-domain HTTP-responses directly (CORS enabled), but it seems that I am not able to read cross-domain HTTP-responses once they load into an IFRAME.

And as if this question is not unsolvable enough, let me exclude these solutions:

  1. easyXDM and similar techniques which require an end-point on the remote domain,

  2. altering the XML response (to include a SCRIPT element),

  3. server-side proxy - I understand that I could have a server-side script on my domain which could serve as a proxy.

So, apart from those two solutions, can this be done?


It can be done!!

It turns out that it is possible to forge a XHR-request (Ajax-request) which imitates a multipart/form-data FORM submit (which is used in the image above to upload the file to the server).

The trick is to use FormData constructor - read this Mozilla Hacks article for more information.

This is how you do it:

// STEP 1
// retrieve a reference to the file
// <input type="file"> elements have a "files" property
var file = input.files[0];

// STEP 2
// create a FormData instance, and append the file to it
var fd = new FormData();
fd.append('file', file);

// STEP 3 
// send the FormData instance with the XHR object
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://remote-domain.com/script.pl', true);
xhr.onreadystatechange = responseHandler;
xhr.send(fd);

The above method executes an asynchronous file-uplaod, which is equivalent to the regular file-upload described in the image above and achieved by submitting this form:

<form action="http://remote-domain.com/script.pl" 
        enctype="multipart/form-data" method="post">
    <input type="file" name="file">
</form>

Like a Boss :)


Just send a cross-domain XHR request with the data from the form instead of submitting the form. CORS is only for the former.

If you must do it the other way, negotiate with the frame using postMessage.

And since the contents of the IFRAME is an XML document, there is no JavaScript code inside the IFRAME which could make use of postMessage or something.

How does that stop you? Include a script element under the HTML or SVG namespace (<script xmlns="http://www.w3.org/1999/xhtml" type="application/ecmascript" src="..."/>) anywhere in the XML.