2016-05-22 6 views
5

Ich habe ein ziemlich seltsames Problem mit Lese-Streams in Node.js arbeiten. Ich verwende SSH2, um eine sftp-Verbindung zwischen mir und einem sftp-Server zu erstellen. Ich versuche dann einen Lese-Stream aus dem SFTP-Stream zu erstellen. Aus dem vom Datenstrom ausgesendeten Ereignis 'Daten' füge ich die Daten an ein Array an. Wenn das Ereignis "Schließen" des Lesestroms auftritt, rufe ich Buffer.concat auf, um concat alle Datenblöcke zu erstellen, die ich in einen Puffer abgerufen habe. Dies ist die gleiche Technik, die in anderen Fragen beschrieben wurde, die hier beim Stapelüberlauf gestellt wurden. Zum Beispiel here. Ich kann die Daten, die ich erhalte, jedoch nicht verwenden. Es sieht so aus, als ob der Puffer 32 Bytes weniger groß ist als die Datei, die ich abzurufen versuche (indem ich die Länge der abgerufenen Daten zähle). Könnte das etwas mit meiner SFTP-Verbindung zu tun haben? Oder wie erstelle ich meinen Lesestream?Lesen von Datei von SFTP-Server mit Node.js und SSH2

Wenn es darauf ankommt, ist die Datei vom Typ zip. Wenn ich versuche, die Datei zu entpacken (in node.js und manuell), nachdem ich sie in den Puffer gelesen habe, funktioniert sie nicht.

Nach der Untersuchung habe ich herausgefunden, dass:

  1. Wenn ich readdir auf die Datei verwenden, die Größe der Datei korrekt ist.
  2. Die Verwendung von FTP (JSFTP) gegen meine Entwicklung FTP-Server funktioniert gut mit der gleichen Technik oben.

Jede Beratung wird geschätzt!

Hier ist mein Code:

 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); 

Antwort

7

So nach vieler Untersuchung schließlich erkennen ich, dass es etwas falsch mit dem letzten Datenbits war, die in dem ‚Daten‘ Ereignis übertragen wurde. Aus meiner Sicht scheint es ein Fehler in der Implementierung des Lesestreams zu sein. Ich konnte dieses Problem umgehen, indem ich einfachere Funktionen (open, fstat, read) in der SSH2-Bibliothek verwendete. Diese Lösung funktioniert für mich. Wollte die Lösung teilen, wenn jemand anderes auf das gleiche Problem stößt.

Arbeitscode:

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_Complete'); 
     } 
    } 
}); 
0

Wenn die Byte-Größe (oder Größe chunk) nicht zwingend notwendig ist, und Sie müssen nur die Datei zu bekommen, denke, es gibt eine viel bessere ist leichter und schneller werdenden Weg (yeah. ..der Knotenpunkt!). Dies ist, wie ich eine Datei kopieren verwenden:

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); 
} 

Als Alternative können Sie auch sftp.fastGet() versuchen, die parallel verwendet liest die Datei schnell zu bringen. fastGet() bietet Ihnen eine Möglichkeit, den Fortschritt des Downloads anzuzeigen (falls gewünscht), abgesehen von der Möglichkeit, die Anzahl der parallelen Lesevorgänge und die Chunk-Größe zu konfigurieren. Um mehr zu erfahren, öffnen Sie diese SFTPStream doc und suchen Sie nach fastGet.

Hier ist ein sehr schnell Code:

sftp.fastGet(remoteFile, localFile, function (err) { 
    if (err) throw err; 
    console.log(`${remoteFile} has successfully download to ${localFile}!`); 
} 

HIH!

+0

das Problem von sftp.fastGet/fastDownload ist, dass es keine Pause-Funktion bieten, sondern Stream kann. –