Node-FTP duplicating operations upon uploading a file

As there are things called ‘callback hell’. It was the only way I can get a file from a server to my vps pc, and upload it. The process was simple:

  1. Download a .json file from the ftp server
  2. Edit the .json file on the pc
  3. Upload the .json file and delete the pc’s copy.

However my problem was this: Although it downloads once, it returns the upload based on how many times I command it during 1 session (command #1, does it once, command#2, does it twice, etc).

I tried to run it as imperative, but gets nullified. Had to resort to callback hell to run the code almost properly. The trigger works to initialize the command, but the command and session goof’d.

((  //declaring my variables as parameters
    ftp=new (require('ftp'))(),
    fs=require('fs'),
    serverFolder='./Path/Of/Server/',
    localFolder='./Path/Of/Local/',
    file='some.json',
    {log}=console
)=>{
    //run server if its ready
    ftp.on('ready',()=>{

       //collect a list of files from the server folder
       ftp.list(serverFolder+file,(errList,list)=>
           errList|| typeof list === 'object' &&
           list.forEach($file=>

              //if the individual file matches, resume to download the file
              $file.name===file&&(
                 ftp.get(serverFolder+file,(errGet,stream)=>
                     errGet||(
                        log('files matched!  cdarry onto the operation...'),
                        stream.pipe(fs.createReadStream(localFolder+file)),
                        stream.once('close',()=>{

                            //check if the file has a proper size
                            fs.stat(localFolder+file,(errStat,stat)=>
                                errStat || stat.size === 0

                                //will destroy server connection if bytes = 0
                                ?(ftp.destroy(),log('the file has no value'))

                                //uploads if the file has a size, edits, and ships
                                :(editThisFile(),
                                    ftp.put(
                                       fs.createReadStream(localFolder+file),
                                       serverFolder+file,err=>err||(
                                          ftp.end(),log('process is complete!')
                                 ))
                                //editThisFile() is a place-holder editor
                                //edits by path, and object                                    
                            )
                        })
                    )
                 )
              )
           )
       );
    });
    ftp.connect({
       host:'localHost',
       password:'1Forrest1!',
       port:'21',
       keepalive:0,
       debug: console.log.bind(console)
    });
})()

The main problem is: it’ll return a copy of the command over and over as ‘carry over’ for some reason.

Edit: although the merits of “programming style” is different than common meta. It all leads to the same issue of callback hell. Any recommendations are needed. For readability, I had help editing my code to ease difficulty. Better Readability version

Answer

The ftp modules API leads to the callback hell. It also hasn’t been maintained for a while and is buggy. Try a module with promises like basic-ftp.

With promises the code flow becomes much easier to reason with and errors don’t require specific handling, unless you want to.

const ftp = require('basic-ftp')
const fsp = require('fs').promises

async function updateFile(localFile, serverFile){
  const client = new ftp.Client()
  await client.access({
     host: 'localHost',
     password: '1Forrest1!',
  })
  await client.downloadTo(localFile, serverFile)
  const stat = await fsp.stat(localFile)
  if (stat.size === 0) throw new Error('File has no size')
  await editThisFile(localFile)
  await client.uploadFrom(localFile, serverFile)
}

const serverFolder = './Path/Of/Server'
const localFolder = './Path/Of/Local'
const file = 'some.json'
updateFile(localFolder + file, serverFolder + file).catch(console.error)

Leave a Reply

Your email address will not be published. Required fields are marked *