Last active
September 15, 2025 22:01
-
-
Save mhokanson/fbe605002678b36c4b7f0dd534b1ac50 to your computer and use it in GitHub Desktop.
Accept files via SFTP in Mirth Connect from a Javascript Reader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var sftpConfigs = [ | |
| { | |
| "name":"SFTP 1", | |
| "host":"127.0.0.1", | |
| "port":"22", | |
| "directory":"/folder/path/to/messages", | |
| "username":"username1", | |
| "password":"password1", | |
| "isBinary": false, | |
| "afterReadAction":{ | |
| "action":"leave" | |
| } | |
| }, | |
| { | |
| "name":"SFTP 2", | |
| "host":"localhost", | |
| "port":"22", | |
| "directory":"/path/to/pickup", | |
| "username":"username2", | |
| "password":"password2", | |
| "isBinary": false, | |
| "afterReadAction":{ | |
| "action":"delete" | |
| } | |
| }, | |
| { | |
| "name":"SFTP 3", | |
| "host":"0.0.0.0", | |
| "port":"22", | |
| "directory":"/SFTPMirth/upload", | |
| "username":"username3", | |
| "password":"password3", | |
| "isBinary": false, | |
| "afterReadAction":{ | |
| "action":"move", | |
| "targetDirectory":"/SFTPMirth/upload/test1/test2" | |
| } | |
| } | |
| ] | |
| importPackage(com.jcraft.jsch); | |
| sftpConfigs = JSON.parse($cfg("SFTPConfigs")) | |
| // Loop through defined configs and check for files | |
| for(var i=0; i < sftpConfigs.length;i++){ | |
| var config = sftpConfigs[i]; | |
| // The next bit is based on Nick Rupley's code at: | |
| // https://forums.mirthproject.io/forum/mirth-connect/support/8410-sftp-client?p=60591#post60591 | |
| // establish a connection to the SFTP site | |
| var jsch = new JSch(); | |
| jsch.setConfig('StrictHostKeyChecking','no'); | |
| var session = jsch.getSession(config.username,config.host,config.port); | |
| session.setPassword(config.password); | |
| session.connect(); | |
| var channel = session.openChannel('sftp'); | |
| channel.connect(); | |
| var remoteWorkingDirectory = channel.pwd(); | |
| channel.cd(config.directory) | |
| var files = channel.ls("*.*"); | |
| for(var f = 0; f < files.size(); f++){ | |
| // Exclude folders and symlinks from processing | |
| if( files.get(f).getAttrs().isDir() != true && | |
| files.get(f).getAttrs().isLink() != true){ | |
| var filename = files.get(f).getFilename(); | |
| var filepath = files.get(f).getLongname(); | |
| // Get the last-modified timestamp for the file | |
| // Also append a trio of zeros to make the timestamp compatible with Date objects | |
| var file_mtime = files.get(f).getAttrs().getMTime() + "000" | |
| var file_age = Date.now() - file_mtime | |
| var inputStream = channel.get(filename) | |
| // Read in the file | |
| if(config.isBinary == true){ | |
| var byteArray = org.apache.commons.io.IOUtils.toByteArray(inputStream) | |
| var b64File = java.util.Base64.getEncoder().encodeToString(byteArray) | |
| var file = new RawMessage(b64File, null, { | |
| "fileDirectory":config.directory + "/" + filename, | |
| "originalFilename":filename, | |
| "transferType":config.transferType | |
| }) | |
| } else { | |
| var fileContents = org.apache.commons.io.IOUtils.toString(inputStream,'UTF-8') | |
| var file = new RawMessage(fileContents, null, { | |
| "fileDirectory":config.directory + "/" + filename, | |
| "originalFilename":filename, | |
| "transferType":config.transferType | |
| }) | |
| } | |
| var msgRoutingResponse = com.mirth.connect.server.userutil.VMRouter().routeMessageByChannelId(channelId, file) | |
| // Trying to work out bad-read detection | |
| // logger.info("msgRoutingResponse: "+JSON.stringify(msgRoutingResponse)) | |
| // The process should have thrown an error if file transfer was unsuccessful prior to this | |
| // preventing deletion of failed transfers once complete | |
| switch(config.afterReadAction.action){ | |
| case "delete": | |
| channel.rm(filename) | |
| break; | |
| case "move": | |
| // Get the relative path to the destination folder | |
| remoteWorkingDirectory = channel.pwd(); | |
| var mkDirPath = config.afterReadAction.targetDirectory | |
| if(mkDirPath.substring(0,remoteWorkingDirectory.length()) == remoteWorkingDirectory){ | |
| mkDirPath = mkDirPath.substring(remoteWorkingDirectory.length()) | |
| } | |
| // Create the destination directory, if needed | |
| // | |
| // As we cannot create subfolders recursively we'll | |
| // need to check for each folder in the destination path | |
| // and create any folders we find missing | |
| var pathParts = mkDirPath.split("/"); | |
| var partialPath = config.directory | |
| for(var p = 0; p < pathParts.length; p++){ | |
| if(pathParts[p] == ""){ | |
| continue; | |
| } | |
| // The current reletive folder we're checking for | |
| partialPath = partialPath + "/" + pathParts[p] | |
| // Try to create the current folder of the partial destination path | |
| // If the folder already exists an error is thrown | |
| try{ | |
| channel.mkdir(partialPath) | |
| } catch (e) { | |
| // Do nothing, as reaching here means the foldr already exists | |
| var folderAlreadyExists | |
| } | |
| } | |
| // Move the file to the destination folder | |
| channel.rename(file.filepath,config.afterReadAction.targetDirectory + "/" + filename) | |
| break; | |
| default: | |
| // Leave things as they are | |
| } | |
| } | |
| } | |
| session.disconnect(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment