API файлов Javascript/HTML5, считывающий последовательные файлы в составные данные формы

Я использую API файлов HTML5 для сборки данных составной формы для отправки XHR в веб-службу. У меня все это работает в FF, у которого есть хороший удобный метод getAsBinary(), включенный в их реализацию файлового API. Это была довольно приятная сделка. В основном получилось:

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

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

sendData(const);

Работал как шарм.

Однако, чтобы заставить его работать в Chrome, мне нужно создать объект FileReader, который обрабатывается немного по-другому. Мне по сути нужно идти:

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);

Что не работает по двум основным причинам. Во-первых, чтение происходит асинхронно, поэтому к тому времени, когда оно доходит до функции sendData(), данные файла не записываются в константную переменную. Во-вторых, переменная const находится вне области видимости внутри обработчика reader.onload. Как бы я ни переделывал код, я, кажется, наткнулся на одно из этих препятствий, и я изо всех сил пытаюсь придумать изящный способ справиться с ним.

Какие-либо предложения?


person R Hill    schedule 19.03.2011    source источник
comment
На самом деле const определенно находится в области видимости, но проблема не в области видимости.   -  person Pointy    schedule 19.03.2011
comment
Кстати, очень хорошо поставленный вопрос. Добро пожаловать в Stackoverflow!   -  person Pointy    schedule 19.03.2011
comment
Спасибо. Пришло время мне спрятаться.   -  person R Hill    schedule 19.03.2011


Ответы (2)


Что вам нужно сделать, так это заставить все обработчики «загрузки» читателя проверить, не являются ли они последними. Когда это произойдет, этот обработчик может вызвать «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]);
}

(Я не совсем понимаю детали того, как эта накопленная "константа" правильно превратится в составной MIME-блоб, но я предполагаю, что вы понимаете :-) Кроме того, и это, вероятно, важно: я думаю вам, вероятно, нужно создать новый экземпляр «FileReader» для каждого файла. Я закодировал это таким образом (на самом деле я только что отредактировал его), но это может быть неправильно, так как я не очень хорошо знаком с API и его семантикой.

person Pointy    schedule 19.03.2011
comment
Я абстрагировался от const. В противном случае это был бы запутанный беспорядок границ и заголовков. Этот бит уже работает. - person R Hill; 19.03.2011
comment
Ну ладно тогда. Альтернативой этому может быть создание чего-то вроде новой функции Deferred в jQuery. Это простая идея, основанная на небольших объектах, которые поддерживают списки дел, и API для других вещей, которые нужно вызывать, когда пришло время уведомить об изменении состояния. Это был бы способ инкапсулировать обслуживание этого готового счетчика. - person Pointy; 19.03.2011
comment
Я определенно изучаю это. Спасибо. - person R Hill; 19.03.2011