Creating an SFTP Utility in SSJS using JSch

I recently had a requirement to write some ssjs utilities to handle basic SFTP functions.  I’m not FTP expert so it was new to me that FTPS has functions built into the Apache Commons FTP library but SFTP does not.  (For more on FTP vs FTP/S vs SFTP, check out this article.)

To create the desired library I found Java Secure Channel by JCraft.com that would allow me to use java libraries in my ssjs to create the desired utility functions.  Using java classes in ssjs is a useful way to extend the functionality of ssjs if your requirements are such that you can’t have your business logic in java.

  1. Import the file into your NSF
  2.  Download the .jar file for JSch.
    • Right-click on your file in Package Explorer
    • Choose Build P ath –> Configure Build Path
    • Choose the “Add JARs” button
    • Navigate to your .jar file from step 1 and select OK
  3. Create a ssjs library for your utility functions.  I’ve functions to do the following tasks:
    • Connect to a server
    • Close the connection
    • Put a file on the server
    • Delete a file from the server
    • Get a list of file names from the server that match a pattern
    • Get a file from the server and return the contents as an input stream

The time consuming part here was in the conversion of the java objects to ssjs.  Aside from that the code is mostly straightforward.


var utilFTP = {

  /*
  * Initialize global variables used in all FTP methods
  * Code uses Java Secure Channel from JCraft
  * Shean McManus | PSC Group | September 2016
  */
  ftp:org.apache.commons.net.ftp.FTP,
  ftpClient:org.apache.commons.net.ftp.FTPClient,
  ftpClientConfig:org.apache.commons.net.ftp.FTPClientConfig,
  ssh:com.jcraft.jsch.JSch,
  sftpSession:com.jcraft.jsch.Session,
  sftpChannel:com.jcraft.jsch.ChannelSftp,

  /*
  * Initialize FTP session
  */
  connect: function(ftpServer:string, ftpUsername:string, ftpPassword:string, ftpKeyChecking:boolean, ftpKeyString:string) {
    try {
      // initialize JSCH object
      this.ssh = new com.jcraft.jsch.JSch();
      // create sftp session
      this.sftpSession = this.ssh.getSession(ftpUsername,ftpServer);
      // set the password
      this.sftpSession.setPassword(ftpPassword);
      // read boolean to implement key checking or not
      if (ftpKeyChecking == true) {
        // get host key string from calling function
        var his:java.io.InputStream = new java.io.ByteArrayInputStream(ftpKeyString.getBytes());
        // add key to known hosts
        this.ssh.setKnownHosts(his);
      } else {
        // disable key checking
        var config:java.util.Properties = new java.util.Properties();
        config.put("StrictHostKeyChecking","no");
        this.sftpSession.setConfig(config);
      }
      // connect to server
      this.sftpSession.connect();
      // get sftp channel
      this.sftpChannel = this.sftpSession.openChannel("sftp");
      // connect
      this.sftpChannel.connect();
      // return value
      return true;
    } catch(e) {
      // INSERT YOUR ERROR LOGGING CODE HERE
      return false;
    }
  },

  /*
  * Close FTP session
  */
  close: function() {
    try {
      // disconnect channel
      if (this.sftpChannel != null) {
        this.sftpChannel.disconnect();
      }
      // disconnect session
      if (this.sftpSession != null) {
        this.sftpSession.disconnect();
      }
      return true;
    } catch(e) {
      // INSERT YOUR ERROR LOGGING CODE HERE
      return false;
    }
  },

  /*
  * Delete file on FTP Server
  */
  deleteFile: function(ftpFilePath,ftpFileName) {
    try {
      this.sftpChannel.rm(ftpFilePath + "\\" + ftpFileName);
      return true;
    } catch(e) {
      // INSERT YOUR ERROR LOGGING CODE HERE
      return false;
    }
  },

  /*
  * Transfer file to FTP server
  */
  putFile: function(ftpFromFilePath:string, ftpFileName:string, ftpToFilePath:string) {
    try {
      // add the file to the ftp server, using double slashes to account for escaping
      this.sftpChannel.put(ftpFromFilePath + "\\" + ftpFileName, ftpToFilePath + "\\" + ftpFileName);
      return true;
    } catch (e) {
      // INSERT YOUR ERROR LOGGING CODE HERE return false;
    }
  },

  /*
  * Retrieve file from FTP server and return the input stream of the file
  */
  getFile: function(ftpFilePath:string,ftpFileName:string) {
    try {
      // get the file and return it into an input stream
      var myInputStream:java.io.FileInputStream = this.sftpChannel.get(ftpFilePath + "\\" + ftpFileName);
      return myInputStream
    } catch(e) {
      // INSERT YOUR ERROR LOGGING CODE HERE return false;
    }
  },

  /*
  * Returns an array of file names that match the prefix string
  */
  getFileNames: function(ftpMode:string,ftpFilePath:string,ftpFilePrefix:string) {
    try {
      // get a list of files that are in the file path
      var ls:java.util.Vector = this.sftpChannel.ls(ftpFilePath);
      var matchedFiles = [];
      // loop through list
      for (entry in ls) {
        // set up pattern to match
        var thisPattern:java.util.regex.Pattern = java.util.regex.Pattern.compile(ftpFilePrefix + '[0-9]{8}.csv');
        // get each entry
        var thisEntry:java.lang.String = entry.getFilename();
        // check if the entry matches the pattern
        var m:java.util.regex.Matcher = thisPattern.matcher(thisEntry);
        // initialize the temporary sequence input stream
        var sis:java.io.SequenceInputStream = null;
        if (m.matches()) {
          // if the file matches, push the contents into the matched files array
          matchedFiles.push(thisEntry)
       }
     }
     // sort the files by filename - most recent will be last
     return matchedFiles.sort();
    } catch(e) {
      // INSERT YOUR ERROR LOGGING CODE HERE return false;
    }
  }
}