Andrew Whalley Andrew Whalley - 4 months ago 41
Node.js Question

Transfer entire directory using ssh2 in Nodejs

I'm just wondering whether it is at all possible to transfer a directory from a unix server to my local machine using the ssh2 module in node.js. I have connected to the remote host and can read the directory as well as transfer single files, but there are 28 folders in the directory which each contain files and sub directories. What I'd like to do is take an exact copy of the main directory from the server to my local machine.

I was using fastGet with single files, but transferring a directory gives:

Error: EISDIR, open __dirname/../localdirectory/
which I think implies I can't use fastGet to get an entire directory. I also tried using the exec command to try and scp it over, but I couldn't work out the syntax for the local directory:

// c is an active connection
c.exec('scp filethatexists.extension /../filepath/newname.extension', function(err, stream) {
if (err) {
console.log("error: " + err);
stream.end;
};
stream.on('data', function(data, extended) {
console.log((extended === 'stderr' ? 'STDERR: ' : 'STDOUT: ') + data);
});
stream.on('end', function() {
console.log('Stream :: EOF');
});
stream.on('close', function() {
console.log('Stream :: close');
});
stream.on('exit', function(code, signal) {
console.log('Stream :: exit :: code: ' + code + ', signal: ' + signal);
c.end();
});
});


This just results in the EOF calling. This code was just me testing If I could get a single file transferring.

Can anyone provide me with any assistance? Thank you in advance.

Answer

A couple of solutions:

  1. You could recursively traverse the directory (making directories and transferring files as needed) using the sftp methods

  2. Tar the directory (compress it too if you want) to stdout (e.g. tar cf - mydir) and then process that incoming stdout data with the tar module (and the built-in zlib module first if you end up compressing the directory).

    // Requires:
    //   * `npm install tar-fs`
    //   * `ssh2` v0.5.x or newer
    var tar = require('tar-fs');
    var zlib = require('zlib');
    
    function transferDir(conn, remotePath, localPath, compression, cb) {
      var cmd = 'tar cf - "' + remotePath + '" 2>/dev/null';
    
      if (typeof compression === 'function')
        cb = compression;
      else if (compression === true)
        compression = 6;
    
      if (typeof compression === 'number'
          && compression >= 1
          && compression <= 9)
        cmd += ' | gzip -' + compression + 'c 2>/dev/null';
      else
        compression = undefined;
    
      conn.exec(cmd, function(err, stream) {
        if (err)
          return cb(err);
    
        var exitErr;
    
        var tarStream = tar.extract(localPath);
        tarStream.on('finish', function() {
          cb(exitErr);
        });
    
        stream.on('exit', function(code, signal) {
          if (typeof code === 'number' && code !== 0) {
            exitErr = new Error('Remote process exited with code '
                                + code);
          } else if (signal) {
            exitErr = new Error('Remote process killed with signal '
                                + signal);
          }
        }).stderr.resume();
    
        if (compression)
          stream = stream.pipe(zlib.createGunzip());
    
        stream.pipe(tarStream);
      });
    }
    
    // USAGE ===============================================================
    var ssh = require('ssh2');
    
    var conn = new ssh();
    conn.on('ready', function() {
      transferDir(conn,
                  '/home/foo',
                  __dirname + '/download',
                  true, // uses compression with default level of 6
                  function(err) {
        if (err) throw err;
        console.log('Done transferring');
        conn.end();
      });
    }).connect({
      host: '192.168.100.10',
      port: 22,
      username: 'foo',
      password: 'bar'
    });