使用node.js,stream和承诺下载文件

这是我的代码片段:

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下载项目。 目前它下载第一个文件,但后来的文件失败。 我是新来的节点,但相当有信心,我的第二个地图function需要返回一个承诺,但我一直无法解决如何多次尝试。 我正在使用bluebird的承诺,但看不到如何使用它和stream。

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

谢谢

我不确定你在哪里被卡住,但指出你的大方向应该足够了:

  • 你有一个pipe道和事件的接口
  • 你需要promisify这个接口。

所以你需要做的是:

  1. 找出什么是下载的“完成”事件。
  2. 创build一个承诺,并在该事件上解决它,在失败的事件上拒绝它。
  3. 回报那个承诺。

Promisifying可以通过几种方式完成:

  • 由诺言库。 Bluebird包含一个非常聪明的promisifier,它使用依赖于JIT的dynamic代码生成 – 它速度非常快 – 但它是为NodeJS“nodeback”情况构build的。 (即错误作为callback的第一个parameter passing。)

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

  • 在Bluebird中使用Promise.method ,这对于轻松实现promises API来说是非常好的,但在这里我们并不是这样。

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

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

 new Promise(function(resolve,reject){ resolve(); // this resolves the promise reject(); // this rejets the promise }); 

请注意,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(); }); }); }). 

您通常应该将promisifications提取到专门的方法。 例如,上面可能是promisifyPipe或类似的。

这是一个promisifiedpipe道:

 //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()) }