Can I make node.js FTP synchronous?

2k Views Asked by At

I have a little FTP script which basically transfer an entire directory tree (by walking it with fs.readdir) to an FTP server one file at a time (I have to do some analysis on each file as it's uploaded hence the one-at-a-time behaviour).

However, the bit that does a single file (there's another bit for directories which uses c.mkdir rather than c.put) looks like this:

console.log('Transferring [' + ival + ']');
var c = new Ftp();
c.on('ready', function() {
    c.put(ival, ival, function(err) {
        console.log(err);
    });
    c.end();
});

As you can see, it's using a very simple method of logging in that failures simply get sent to the console.

Unfortunately, since the FTPs are done asynchronously, errors are being delivered to the console in a sequence totally unrelated to the file name output.

Is there a way to force the FTP to be done synchronously so that errors would immediately follow the file name? Basically, I want the entire sequence from the initial console.log to the final }); to be done before moving on to the next file.

3

There are 3 best solutions below

13
On BEST ANSWER

Even if there is, it's not recommended. You generally don't want to block the event loop with such a long synchronous operation.

What would probably be more useful is using recursion or Promises to ensure that things happen in a sequence.

Example:

let ivals = [/* lots of ivals here */];
function putItems(ivals) {
    let ival = ivals[0];
    console.log('Transferring [' + ival + ']');
    var c = new Ftp();
    c.on('ready', function() {
        c.put(ival, ival, function(err) {
            console.log(err);
            c.end();
            // Don't continue if we're out of items.
            if (ivals.length === 1) { return; } 
            putItems(ivals.slice(1)); // Call again with the rest of the items.
        });
    });
}

putItems(ivals);

It can probably be done more intelligently by using a nested function and a single FTP context. But you get the point.

3
On

Why dont you just push the errors to an array, and when all uploads are done, you will have that array with all those errors in order ?

I will do something like this:

var errArray = [];
console.log('Transferring [' + ival + ']');
var c = new Ftp();
c.on('ready', function() {
  c.put(ival, ival, function(err) {
    errArray.push( err );
  });
  c.end();
});
c.on('end', function() {
  errArray.forEach( function( err ){
    console.log( err );
  })
});
4
On

Without making things synchronous, you can solve your error logging problem by just logging the name with the error. You can just wrap this in a closure so you can keep track of ival that goes with a particular error:

(function(ival) {
    console.log('Transferring [' + ival + ']');
    var c = new Ftp();
    c.on('ready', function() {
        c.put(ival, ival, function(err) {
            console.log('[' + ival + ']', err);
        });
        c.end();
    });
})(ival);