使用node.js,streams和promises下载文件

Jon*_*Jon 8 javascript stream node.js promise bluebird

这是我的代码片段:

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).
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用promises通过FTP下载项目.目前它下载了第一个文件,但后来的文件失败了.我是节点的新手,但相当有信心我的第二个地图功能需要返回一个承诺但是我经过多次尝试后无法弄清楚如何.我bluebird用于承诺,但无法看到如何使用它和流.

你能指出我正确的方向吗?

谢谢

Ben*_*aum 15

我不确定你到底在哪里,但指向你的大方向应该足够了:

  • 您有一个适用于管道和事件的界面
  • 你需要宣传那个界面.

所以你需要做的是:

  1. 找出下载的"完成"事件.
  2. 创建一个承诺并在该事件上解决它,在失败的事件上拒绝它.
  3. 回报那个承诺.

可以通过以下几种方式实现宣传:

  • 由诺斯库.Bluebird包含一个非常聪明的promisifier使用动态代码生成依赖于JIT - 它非常快 - 但它是为NodeJS"nodeback"情况而构建的.(即错误作为回调的第一个参数传递.)

  • 使用Deferred对象.通常这种方式更容易出错.

  • Promise.method在Bluebird中使用,这对于轻松地宣传API非常有用,但在这里并不是我们的理由.

  • 使用Promise构造函数.这就是我们在这里要做的.这也是标准投诉.

通常,promise构造函数的接口是:

new Promise(function(resolve,reject){

    resolve(); // this resolves the promise
    reject(); // this rejets the promise
});
Run Code Online (Sandbox Code Playgroud)

请注意,promisifying事件发射器只有在完成事件时触发并且执行一次才能正常工作.承诺是一次,一旦他们解决,他们就无法改变国家.事件可以多次触发.宣传诸如"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();
        });
    });
}).
Run Code Online (Sandbox Code Playgroud)

您通常应该将promisifications提取到专用方法中.例如,以上可以是promisifyPipe或类似的.