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.
- Import the file into your NSF
- 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
- 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; } } }
“The time consuming part here was in the conversion of the java objects to ssjs. Aside from that the code is mostly straightforward.” you like to punish yourself, don’t you? was it no option to keep the code in Java? also less painful in development and debugging
LikeLike
The client requirements demanded ssjs in this case, otherwise you are absolutely correct.
LikeLike