节点FTP从一台服务器下载文件并上传到另一台服务器

时间:2018-10-31 19:32:01

标签: javascript node.js ftp stream

我已经四处寻找了一段时间,this是我在互联网上发现的与我的问题有关的唯一资源。我正在尝试从一台ftp服务器下载文件,然后使用promises逐个将它们上传到另一台ftp服务器,而在此过程中不必将文件保存在本地。

首先,我从ftp模块中递归地调用client.List()以获取文件路径数组,我需要从源ftp服务器下载文件路径。效果很好。

getRecursively(client, path) {
    var _this = this;
    let downloadList = [];
    let paths = [];
    let promise = new Promise((resolve, reject) => {
        client.list(path, function(err, list) {
            async function loop() {
                for (var i = 0; i < list.length; i++) {
                    if (list[i].type == 'd') {
                        let _list = await _this.getRecursively(client, path + '/' + list[i].name)
                        downloadList = downloadList.concat(_list);
                    } else {
                        if ( list[i].name.match(/\.(jpg|jpeg)$/i) ) {
                            downloadList.push({path: path, name: list[i].name});
                        }
                    }
                }
                console.log("One complete");
                resolve(downloadList);

            }

            loop();
        })
    })
    return promise;

}

接下来,我将遍历文件路径列表并发送使用es6-promise-pool模块限制的promise,因此现在将其并发限制设置为10。

这是每个诺言的样子:

getAndInsert(file) {
    let _this = this;
    let promise = new Promise((resolve, reject) => {
        let c = new Client();
        c.on('ready', () => {
            let d = new Client();
            d.on('ready', () => {
                c.get(file.path + '/' + file.name, function(err, stream) {
                    if (err) {console.log(err); console.log("FILE NAME: " + file.name)}
                    d.put(stream.pipe(passThrough()), '/images/' + file.name, function() {
                        _this.uploadCount += 1;
                        _this.uploadedImages.push(file.name)
                        console.log(_this.uploadCount + '/' + _this._list.length + " uploaded.")
                        c.end();
                        d.end();
                        resolve(true);
                    });

                });

            })

            d.on('error', (err) => {
                if (err) console.log(err);
                _this.onCompleteCallback();
            })

            d.connect(destinationFTP);
        })

        c.on('error', (err) => {
            if (err) console.log(err);
            _this.onCompleteCallback();
        })

        c.connect(sourceFTP);

    })
    return promise;
}

每个promise都与源和目标ftp服务器建立自己的连接。调用stream时,我也在使用Transform模块的d.put(stream.pipe(passThrough())对象。这是该功能。

    const passThrough = () => {
       var passthrough = new Transform();
       passthrough._transform = function(data, encoding, done) {
           this.push(data);
           done();
       };
       return passthrough;
   }

最后,这是释放承诺的主要代码。

*buildPromises(list) {
    for (let i = 0; i < list.length; i++) {
        yield this.getAndInsert(list[i]);
    }
}

let iterator = _this.buildPromises(list);
var pool = new PromisePool(iterator, 10);
pool.start()
    .then(function(){
        console.log("Finished")
    }).catch((err) => {
        console.log(err);
        console.log("error processing pool promise");
    })

这将遍历并构建列表,但是当我发送承诺时,出现以下错误:

Error: write after end
at writeAfterEnd (_stream_writable.js:236:12)
at Transform.Writable.write (_stream_writable.js:287:5)
at Socket.ondata (_stream_readable.js:639:20)
at emitOne (events.js:116:13)
at Socket.emit (events.js:211:7)
at Socket.Readable.read (_stream_readable.js:475:10)
at flow (_stream_readable.js:846:34)
at Transform.<anonymous> (_stream_readable.js:707:7)
at emitNone (events.js:106:13)

它可能经过5次,然后出错,有时甚至更多,但这似乎很一致。我也注意到有时我会收到类似的错误消息,说“文件已在使用中”,但是上传的每个文件都有一个唯一的名称。感谢您的帮助,如果您需要更多信息,我会尽力提供更多信息。谢谢。

1 个答案:

答案 0 :(得分:0)

所以我找到了解决方案。在我的getAndInsert()函数中正在做

c.get(file.path + '/' + file.name, function(err, stream) {
    if (err) {console.log(err); console.log("FILE NAME: " + file.name)}
    d.put(stream.pipe(passThrough()), '/images/' + file.name, function() {
       _this.uploadCount += 1;
       _this.uploadedImages.push(file.name)
       console.log(_this.uploadCount + '/' + _this._list.length + " uploaded.")
       c.end();
       d.end();
       resolve(true);
    });
});

问题出在stream.pipe(passThrough())stream似乎已经结束之后,我似乎在尝试写作。这就是解决我的问题的方法:

let chunks = [];
stream.on('data', (chunk) => {
     chunks.push(chunk);
})
stream.on('end', () => {
   d.put(Buffer.concat(chunks), '/images/' + file.name, function() {
      _this.uploadCount += 1;
      _this.uploadedImages.push(file.name)
      console.log(_this.uploadCount + '/' + _this._list.length + " uploaded.")
      c.end();
      d.end();
      resolve(true);
   });
})

当流中有新数据可用时,将其推送到名为chunks的数组。流结束后,调用.put并传递Buffer.concat(chunks)

希望这可以帮助遇到类似问题的任何人。