2016-05-22 12 views
5

Mam dość dziwny problem ze strumieniami odczytu w pliku Node.js. Używam SSH2 do utworzenia połączenia sftp między mną a serwerem sftp. Następnie próbuję utworzyć strumień odczytu ze strumienia sftp. Z emitowanego zdarzenia "data" strumienia odczytu dołączam dane do tablicy. Kiedy wystąpi zdarzenie "zamknij" strumienia odczytu, wykonuję wywołanie Buffer.concat, aby utworzyć wszystkie fragmenty danych, które pobrałem do jednego bufora. Jest to ta sama technika opisana w innych pytaniach zadawanych tutaj przy przepełnieniu stosu. Na przykład here. Jednak nie mogę użyć danych, które pobieram. Wygląda na to, że bufor ma rozmiar o 32 bajty mniejszy niż plik, który próbuję pobrać (od zliczania długości pobieranych danych). Czy to może mieć coś wspólnego z moim połączeniem SFTP? Albo w jaki sposób mogę utworzyć strumień do odczytu?Czytanie pliku z serwera SFTP przy użyciu Node.js i SSH2

Jeśli to ważne, plik jest typu zip. Kiedy próbuję rozpakować plik (w pliku node.js i ręcznie) po odczytaniu go do bufora, nie działa.

Po zbadaniu znalazłem, że:

  1. Gdy używam readdir na pliku, rozmiar pliku jest poprawna.
  2. Używanie FTP (JSFTP) przeciwko mojemu rozwojowi Serwer FTP działa dobrze, używając tej samej techniki powyżej.

Każda rada jest doceniana!

Oto mój kod:

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

Odpowiedz

7

Więc po wielu dochodzenia w końcu zrozumiał, że coś było nie tak z ostatnich bitów danych, które zostały przekazane w przypadku „dane”. Z mojego zrozumienia wynika, że ​​jest to błąd w implementacji strumienia odczytu. Byłem w stanie obejść ten problem, używając bardziej uproszczonych funkcji (open, fstat, read) w bibliotece SSH2. To rozwiązanie działa dla mnie. Chciałem udostępnić rozwiązanie, jeśli ktoś inny napotka na ten sam problem.

kod robocza:

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

Jeśli rozmiar bajtów (lub wielkość kawałek) nie jest obowiązkowe i wystarczy pobrać plik, Chyba nie jest dużo lepiej lżejszy i-szybszy sposób (yeah. .. sposób nodejs!). W ten sposób mogę używać, aby skopiować plik:

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

Jako alternatywę, można także spróbować sftp.fastGet() który wykorzystuje równolegle czyta się szybko przynieść plik. fastGet() zapewnia sposób pokazywania postępu pobierania (jeśli jest to potrzebne), oprócz możliwości skonfigurowania liczby równoległych odczytów i wielkości porcji. Aby dowiedzieć się więcej, otwórz tę SFTPStream doc i wyszukaj numer fastGet.

Tutaj jest bardzo szybki kod:

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

HIH!

+0

Problem sftp.fastGet/fastDownload jest taki, że nie zapewnia funkcji pauzy, ale strumień może. –

Powiązane problemy