Javascript 使用Node.js和SSH2从SFTP服务器读取文件
我在Node.js中处理读取流时遇到了一个非常奇怪的问题。我正在使用SSH2在我和一个sftp服务器之间创建一个sftp连接。然后,我尝试从sftp流创建一个读取流。从读取流发出的“data”事件中,我将数据附加到数组中。当读流的“关闭”事件发生时,我调用Buffer.concat创建concat,将我检索到的所有数据块集中到一个缓冲区中。这与stack overflow中的其他问题中描述的技术相同。例如但是,我无法使用检索到的数据。看起来缓冲区的大小比我试图检索的文件小32字节(从计算检索到的数据长度来看)。这可能与我的SFTP连接有关吗?或者我如何创建我的读取流 如果重要的话,该文件是zip类型的。当我试图在将文件读取到缓冲区后解压文件(在node.js和手动解压)时,它不起作用 经过调查,我发现:Javascript 使用Node.js和SSH2从SFTP服务器读取文件,javascript,node.js,buffer,sftp,readable,Javascript,Node.js,Buffer,Sftp,Readable,我在Node.js中处理读取流时遇到了一个非常奇怪的问题。我正在使用SSH2在我和一个sftp服务器之间创建一个sftp连接。然后,我尝试从sftp流创建一个读取流。从读取流发出的“data”事件中,我将数据附加到数组中。当读流的“关闭”事件发生时,我调用Buffer.concat创建concat,将我检索到的所有数据块集中到一个缓冲区中。这与stack overflow中的其他问题中描述的技术相同。例如但是,我无法使用检索到的数据。看起来缓冲区的大小比我试图检索的文件小32字节(从计算检索到的
var Client = require('ssh2').Client;
var m_ssh2Credentials = {
host: config.ftpHostName,
port: config.ftpPort,
username: config.ftpUser,
password: config.ftpPassword,
readyTimeout: 20000,
algorithms: { cipher: ["3des-cbc", "aes256-cbc", "aes192-cbc","aes128-cbc"]}
};
...
var conn = new Client();
var dataLength = 0;
conn.on('ready', function() {
conn.sftp(function(err, sftp) {
if (err) {
writeToErrorLog("downloadFile(): Failed to open SFTP connection.");
} else {
writeToLog("downloadFile(): Opened SFTP connection.");
}
var streamErr = "";
var dataLength = 0;
var stream = sftp.createReadStream(config.ftpPath + "/" + m_fileName)
stream.on('data', function(d){
data.push(d);
dataLength += d.length;
});
.on('error', function(e){
streamErr = e;
})
.on('close', function(){
if(streamErr) {
writeToErrorLog("downloadFile(): Error retrieving the file: " + streamErr);
} else {
writeToLog("downloadFile(): No error using read stream.");
m_fileBuffer = Buffer.concat(data, dataLength);
writeToLog("Data length: " + dataLength);
writeToLog("downloadFile(): File saved to buffer.");
}
conn.end();
});
})
})
.on('error', function(err) {
writeToErrorLog("downloadFile(): Error connecting: " + err);
}).connect(m_ssh2Credentials);
因此,经过大量的调查,我终于意识到在“数据”事件中传输的最后几位数据有问题。据我所知,这似乎是读流实现中的一个错误。通过使用SSH2库中更简单的函数(open、fstat、read),我能够绕过这个问题。这个解决方案对我有效。如果其他人遇到同样的问题,希望分享解决方案 工作代码:
sftp.open(config.ftpPath + "/" + m_fileName, "r", function(err, fd) {
sftp.fstat(fd, function(err, stats) {
var bufferSize = stats.size,
chunkSize = 16384,
buffer = new Buffer(bufferSize),
bytesRead = 0,
errorOccured = false;
while (bytesRead < bufferSize && !errorOccured) {
if ((bytesRead + chunkSize) > bufferSize) {
chunkSize = (bufferSize - bytesRead);
}
sftp.read(fd, buffer, bytesRead, chunkSize, bytesRead, callbackFunc);
bytesRead += chunkSize;
}
var totalBytesRead = 0;
function callbackFunc(err, bytesRead, buf, pos) {
if(err) {
writeToErrorLog("downloadFile(): Error retrieving the file.");
errorOccured = true;
sftp.close(fd);
}
totalBytesRead += bytesRead;
data.push(buf);
if(totalBytesRead === bufferSize) {
m_fileBuffer = Buffer.concat(data);
writeToLog("downloadFile(): File saved to buffer.");
sftp.close(fd);
m_eventEmitter.emit(' downloadFile_Completed ');
}
}
})
});
sftp.open(config.ftpPath+“/”+m_文件名,“r”,函数(err,fd){
sftp.fstat(fd,函数(err,stats){
var bufferSize=stats.size,
chunkSize=16384,
缓冲区=新缓冲区(缓冲区大小),
字节读取=0,
ErrorOccursed=false;
while(bytesRead缓冲区大小){
chunkSize=(bufferSize-bytesRead);
}
sftp.read(fd,buffer,bytesRead,chunkSize,bytesRead,callbackFunc);
字节读取+=块大小;
}
var totalBytesRead=0;
函数callbackFunc(err、bytesRead、buf、pos){
如果(错误){
writeToErrorLog(“downloadFile():检索文件时出错。”);
ErrorOccursed=true;
sftp.关闭(fd);
}
totalBytesRead+=字节读取;
数据推送(buf);
if(totalBytesRead==缓冲区大小){
m_fileBuffer=Buffer.concat(数据);
writeToLog(“downloadFile():保存到缓冲区的文件”);
sftp.关闭(fd);
m_eventEmitter.emit('downloadFile_Completed');
}
}
})
});
如果字节大小(或块大小)不是强制性的,您只需要获取文件,那么您可以猜测有一种更轻、更快的方法(是的……nodejs方法!)。这是我用来复制文件的方式:
function getFile(remoteFile, localFile) {
conn.on('ready', function () {
conn.sftp(function (err, sftp) {
if (err) throw err;
var rstream = sftp.createReadStream(remoteFile);
var wstream = fs.createWriteStream(localFile);
rstream.pipe(wstream);
rstream.on('error', function (err) { // To handle remote file issues
console.log(err.message);
conn.end();
rstream.destroy();
wstream.destroy();
});
rstream.on('end', function () {
conn.end();
});
wstream.on('finish', function () {
console.log(`${remoteFile} has successfully download to ${localFile}!`);
});
});
}).connect(m_ssh2Credentials);
}
或者,您也可以尝试使用并行读取来快速读取文件的sftp.fastGet()
fastGet()
提供了一种显示下载进度(如果需要)的方法,还提供了一种配置并行读取次数和块大小的方法。要了解更多信息,请打开此文件并搜索fastGet
下面是一个非常快速的代码:
sftp.fastGet(remoteFile, localFile, function (err) {
if (err) throw err;
console.log(`${remoteFile} has successfully download to ${localFile}!`);
}
嗨 sftp.fastGet/fastDownload的问题在于它不提供暂停功能,但流可以。