4

I'm using the HTML5 File API to assemble multipart form data for submission by XHR to a web service. I have the whole thing working in FF, which has a nice convenient getAsBinary() method included in their implementation of the file API. This was a pretty sweet deal. It basically went:

var const;  // constructor
const += headers;
const += field_data;

for(var i = 0; i < files.length; i++)
{
   const += files[i].getAsBinary();
}

sendData(const);

Worked like a charm.

To get it working in Chrome, though, I have to make a FileReader object, which handles a bit differently. I essentially have to go:

var const;  // constructor
const += headers;
const += field_data;

var reader = new FileReader();

for(var i = 0; i < files.length; i++)
{
    reader.onload = (function(file)
    {
       const += file.target.result;   // const is not in scope in this anonymous function!
    }
    reader.readAsBinaryString(files[i]);
}

sendData(const);

Which doesn't work, for two main reasons. Firstly, the read happens asynchronously, so by the time it gets to the sendData() function, the file data isn't written to the const variable. Secondly, the const variable is out of scope inside the reader.onload handler. However I re-jig the code, I seem to come across one of these obstacles, and I'm struggling to come up with a graceful way of handling it.

Any suggestions?

1
  • Actually "const" definitely is in scope, but scope is not the problem. Commented Mar 19, 2011 at 14:42

2 Answers 2

3

What you're going to have to do is have the reader "load" handlers all check to see whether they're the last one to run. When that happens, then that handler can call "sendData()".

var const;  // constructor
const += headers;
const += field_data;

var reader;
var finished = 0;
for(var i = 0; i < files.length; i++)
{
    reader = new FileReader();
    reader.onload = function(file)
    {
       const += file.target.result;
       if (++finished === files.length)
         sendData(const);
    };
    reader.readAsBinaryString(files[i]);
}

(I don't fully understand the details of how that accumulated "const" thing will correctly turn into a multipart MIME blob, but I presume that you do :-) Also, and this is probably important: I think you probably need to make a new "FileReader" instance for each file. I coded this that way (actually I just edited it) but that may be incorrect, as I'm not that familiar with the API and its semantics.

Sign up to request clarification or add additional context in comments.

3 Comments

I abstracted the const thing. Otherwise it would have been a confusing mess of boundaries and headers. That bit already works.
Ah OK then. An alternative to this would be to build up something like the new "Deferred" feature in jQuery. It's a simple idea based around little objects that maintain lists of "things to do", and APIs for other things to call when it's time to notify a state change. That would be a way to encapsulate the maintenance of that "finished" counter.
I'm definitely having a look into that. Thanks.
1

Have you considered xhr.send(FormData)? See my response here: upload to php $_FILE from chrome extension

You can append files to a FormData object and send that via xhr. The browser constructs the multi-part request for you. I believe this is available in FF4 and has been in Chrome for some time.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.