Загрузка файлов с помощью node.js, потоков и обещаний

Вот фрагмент моего кода:

var processListing = function (directoryItems) {
    console.log('foreach');
    var itemsToDownload = [];
    directoryItems.forEach(function (element, index, array) {
        //Ignore directories
        if (element.type === 'd') {
            console.log('directory ' + element.name);
            return;
        }
        //Ignore non zips
        if (path.extname(element.name) !== '.zip') {
            console.log('ignoring ' + element.name);
            return;
        }
        //Download zip
        itemsToDownload.push({
            source: element.name,
            destination: element.name
        });
        //aftpSystem.downloadFile(element.name, element.name);
    });
    console.log('after foreach');
    return itemsToDownload;
};

var getFiles = function () {
    console.log('got files');
    return fs.readdirAsync(process.cwd() + "/zips/").then(function (files) {
        return files.filter(function (filename) {
            return path.extname(filename) === '.zip';
        });
    });
};

    aFtpClient. //this has been promisified
    listAsync(). //so calls methodAsync
    then(processListing).
    map(function (object) {
        return processItem(object).then(function (processResult) {
            return {
                input: object,
                result: processResult
            };
        });
    }).
    map(function (downloadItem) {
        console.log('downloading files');

        downloadItem.result.once('close', function () {
            console.log('closed');
        });

        return downloadItem.result.pipe(fs.createWriteStream(process.cwd() + "/zips/" + downloadItem.input.destination));
    }).
    then(getFiles).

Я пытаюсь использовать обещания для загрузки элементов через FTP. В данный момент он загружает первый файл, но затем не работает с последующими файлами. Я новичок в узле, но довольно уверен, что моя вторая функция карты должна возвращать обещание, однако я не смог разобраться, как после многочисленных попыток. я используюbluebird для обещаний, но не вижу, как работать с ним и потоков.

Не могли бы вы указать мне правильное направление?

Спасибо

 Bergi18 мар. 2014 г., 12:59
Я думаю, что вы возвращаете поток, где вы на самом деле хотели вернуть обещание.
 Jon18 мар. 2014 г., 14:23
Да, я согласен, я поставил это в вопросе, но не уверен, как это сделать с потоком

Ответы на вопрос(2)

//example: promisified_pipe(foo, fs.createWriteStream(somepath))enter code here
function promisified_pipe(response, file) {
let ended = false;

return new Promise(function(resolve, reject) {
    response.pipe(file);

    function nice_ending() {
      if (!ended) {
        ended = true;
        resolve();
      }
    }

    function error_ending() {
      if (!ended) {
        ended = true;
        reject("file error");
      }
    }

    file.on('finish', nice_ending);
    file.on('end', nice_ending);
    file.on('error', error_ending);
    file.on('close', error_ending);
  }).finally(() => file.close())
}
Решение Вопроса

где именно вы застряли, но достаточно указать вам общее направление:

У вас есть интерфейс, который работает с каналом и событиямиВам нужноpromisify этот интерфейс.

Итак, что вам нужно сделать, это:

Узнайте, что является «завершением» события загрузки.Создайте обещание и разрешите его в этом событии, отклоните его в случае неудачного события.Верни это обещание.

Обещание может быть сделано несколькими способами:

По библиотеке обещаний. Bluebird содержит действительно умный обещатель, использующий динамическую генерацию кода, которая опирается на JIT - этоочень быстро - но он построен для случая NodeJS "nodeback". (т.е. ошибка передана в качестве первого аргумента обратного вызова.)

С использованиемDeferred объект. Обычно этот способ более подвержен ошибкам.

С помощьюPromise.method в Bluebird, который отлично подходит для многообещающих API-интерфейсов, но на самом деле это не наш случай.

С использованиемPromise конструктор. Это то, что мы будем делать здесь. Это также стандартная жалоба.

Как правило, интерфейс конструктора обещаний:

new Promise(function(resolve,reject){

    resolve(); // this resolves the promise
    reject(); // this rejets the promise
});

Обратите внимание, что многообещающие источники событий работают только хорошокогда они стреляют по финишу и делают это один раз, Обещания являются однократными, когда они улаживаются, они не могут изменить состояние. События могут срабатывать несколько раз. Это прекрасно, чтобы обещать такие вещи, как"load" события или"finished" события - но не обещайте того, что повторяется несколько раз.

Ваша вторая карта должна выглядеть примерно так:

map(function (downloadItem) {
    console.log('downloading files');

    downloadItem.result.once('close', function () {
        console.log('closed');
    });
    var pipeAction = downloadItem.result.pipe(fs.createWriteStream(process.cwd() + "/zips/" + downloadItem.input.destination));
    return new Promise(function(resolve,reject){
        pipeAction.on("end",function(){ //waits for data to be consumed
             // pipe has ended here, so we resolve the promise
             resolve();
        });
    });
}).

Обычно вы должны извлекать обещания в специальные методы. Например, выше может бытьpromisifyPipe или похожие.

 Jon18 мар. 2014 г., 18:48
изменяя getFiles для принятия параметра и регистрируя его, он показывает [undefined, undefined]
 Jon18 мар. 2014 г., 15:15
Вот Это Да! Это отличный ответ, и я действительно ценю его, и я от него многое отойду. К сожалению, он все еще не загружается должным образом, но, скорее всего, мой код noob. Обновили мой вопрос с полным кодом
 Jon18 мар. 2014 г., 15:53
Я использую этоnpmjs.org/package/ftp Похоже, закрытие мероприятия будет работать
 Jon18 мар. 2014 г., 18:34
нет событие закрытия вызывается дважды (у меня 2 файла). Моя функция getFiles ничего не делает с результатом карты. Он просто читает файлы в директории, надеюсь, куда они скачали
 Jon18 мар. 2014 г., 18:45
я добавил getFiles к вопросу
 Benjamin Gruenbaum18 мар. 2014 г., 16:11
@Esailija Я думал об этом, но таким образом я не смог бы так легко написать комментарий в коде;)
 arcseldon13 июн. 2014 г., 20:13
Этот ответ был слишком общим. Когда речь идет о потоках, какие есть методы «ловушки» для объявления методов обещания и обещания, чтобы обеспечить охват всех случаев? Событие "конец" может быть правильным здесь. (например, некоторые «закрытые» события затем называют «завершением» и т. д.). Некоторые разъяснения приветствуются. Обещание, конкретное для описания, легко.
 Jon18 мар. 2014 г., 15:18
Я добавил код в ваше «конечное» событие, чтобы выйти из системы, но он туда не дошел. Я тоже пытался добавить {end: false} в канал
 Benjamin Gruenbaum18 мар. 2014 г., 15:34
Хм, а как насчет закрытия мероприятия? Не все потоки стреляют"end" и я не уверен, какой именно интерфейс вы используете ..
 Jon18 мар. 2014 г., 18:53
на самом деле иногда его регистрируют один раз, иногда дважды: s
 Benjamin Gruenbaum18 мар. 2014 г., 16:06
о да, мой плохой, поэтому привык к автоматическим посылкам и обработчикам запросов :) Хороший улов.
 Jon19 мар. 2014 г., 10:46
Я хочу пометить его как ответивший, спасибо за вашу помощь, но я до сих пор не могу понять, почему он загружает только один файл за раз.
 Benjamin Gruenbaum18 мар. 2014 г., 16:56
@Jon Если вы поместите журнал в обработчик событий - он будет только один раз? Кроме того, содержит ли возвращаемое отображение правильные элементы?
 Esailija18 мар. 2014 г., 16:10
ВозможноpipeAction.on("end", resolve);
 Jon18 мар. 2014 г., 16:51
хм, что-то странное происходит с этой загрузкой .. только 1 файл управляет
 Jon18 мар. 2014 г., 16:02
Я думаю, что ваш код должен бытьresolve,reject, Теперь он срабатывает в этой функции, но только один раз. не несколько раз, если несколько файлов для загрузки

Ваш ответ на вопрос