The following post is aimed at familiarizing our readers with writing Java code to ultimately be able to connect and interact with SFTP servers. SFTP is a commonly used, standard, and secure protocol, that's main objective is to provide safe file and data transfer. When setting up a connection with the server, there are only a few simple steps to learn and we hope that by the end of this post, you'll know how to do them with your eyes closed.
Requirements
There are several Java libraries that can be used to connect to SFTP servers. We’ll be using Jsch, a pure Java implementation of SSH2. The following examples are just some of the number of ways to add JSCH to your java project:
Using Maven:
<dependency>
<groupId>com.github.mwiede</groupId>
<artifactId>jsch</artifactId>
<version>0.2.0</version>
</dependency>
Using Gradle:
implementation 'com.github.mwiede:jsch:0.2.0'
Connecting to SFTP
For the sake of this post, we’ll be using an environment variable named SFTPTOGO_URL that contains all of the information required to connect to an SFTP server in a URI format: sftp://user:password@host. The variable is parsed in order to extract the URI parts using URI.parse, and the remote server’s host key is verified automatically using ~/.ssh/known_hosts
.
When instantiating the SftpClient class, pass the host, username, and optionally the port (default is 22). To authenticate with a password, use the authPassword
function. To authenticate using public key authentication, pass the path along to the private key and the pass string to the authKey
function.
package com.sftptogo;
import com.jcraft.jsch.*;
import com.sftptogo.util.Util;
import java.util.Properties;
import java.util.Vector;
/**
* A simple SFTP client using JSCH http://www.jcraft.com/jsch/
*/
public final class SftpClient {
private final String host;
private final int port;
private final String username;
private final JSch jsch;
private ChannelSftp channel;
private Session session;
/**
* @param host remote host
* @param port remote port
* @param username remote username
*/
public SftpClient(String host, int port, String username) {
this.host = host;
this.port = port;
this.username = username;
jsch = new JSch();
}
/**
* Use default port 22
*
* @param host remote host
* @param username username on host
*/
public SftpClient(String host, String username) {
this(host, 22, username);
}
/**
* Authenticate with remote using password
*
* @param password password of remote
* @throws JSchException If there is problem with credentials or connection
*/
public void authPassword(String password) throws JSchException {
session = jsch.getSession(username, host, port);
//disable known hosts checking
//if you want to set knows hosts file You can set with jsch.setKnownHosts("path to known hosts file");
var config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setPassword(password);
session.connect();
channel = (ChannelSftp) session.openChannel("sftp");
channel.connect();
}
public void authKey(String keyPath, String pass) throws JSchException {
jsch.addIdentity(keyPath, pass);
session = jsch.getSession(username, host, port);
//disable known hosts checking
//if you want to set knows hosts file You can set with jsch.setKnownHosts("path to known hosts file");
var config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
channel = (ChannelSftp) session.openChannel("sftp");
channel.connect();
}
}
Listing Files
Now that we have a working connection, we can use it to list files on the remote SFTP server. Our wrapper function listFiles
calls the channel’s ls
function to retrieve the list of remote files, iterate over them and print each file’s permissions, name, and size.
/**
* List all files including directories
*
* @param remoteDir Directory on remote from which files will be listed
* @throws SftpException If there is any problem with listing files related to permissions etc
* @throws JSchException If there is any problem with connection
*/
@SuppressWarnings("unchecked")
public void listFiles(String remoteDir) throws SftpException, JSchException {
if (channel == null) {
throw new IllegalArgumentException("Connection is not available");
}
System.out.printf("Listing [%s]...%n", remoteDir);
channel.cd(remoteDir);
Vector<ChannelSftp.LsEntry> files = channel.ls(".");
for (ChannelSftp.LsEntry file : files) {
var name = file.getFilename();
var attrs = file.getAttrs();
var permissions = attrs.getPermissionsString();
var size = Util.humanReadableByteCount(attrs.getSize());
if (attrs.isDir()) {
size = "PRE";
}
System.out.printf("[%s] %s(%s)%n", permissions, name, size);
}
}
Upload File
The next step is to upload a file. Our class’s uploadFile
takes two arguments: localPath (the file to upload) and remotePath (where to upload it to on the server). An example call would look like this: client.uploadFile("./local.txt", "/remote.txt");
/**
* Upload a file to remote
*
* @param localPath full path of location file
* @param remotePath full path of remote file
* @throws JSchException If there is any problem with connection
* @throws SftpException If there is any problem with uploading file permissions etc
*/
public void uploadFile(String localPath, String remotePath) throws JSchException, SftpException {
System.out.printf("Uploading [%s] to [%s]...%n", localPath, remotePath);
if (channel == null) {
throw new IllegalArgumentException("Connection is not available");
}
channel.put(localPath, remotePath);
}
Download File
As we finalize the process, we just need to download our files. The downloadFile
function also takes two arguments: remotePath (file to download) and localPath (where to download the file to). You would call the function like so: client.downloadFile("/remote.txt", "./download.txt");
/**
* Download a file from remote
*
* @param remotePath full path of remote file
* @param localPath full path of where to save file locally
* @throws SftpException If there is any problem with downloading file related permissions etc
*/
public void downloadFile(String remotePath, String localPath) throws SftpException {
System.out.printf("Downloading [%s] to [%s]...%n", remotePath, localPath);
if (channel == null) {
throw new IllegalArgumentException("Connection is not available");
}
channel.get(remotePath, localPath);
}
The Whole Thing
All done! If you would like to run the entire program from start to finish, follow the instructions on how to build and run the project here
package com.sftptogo;
import com.jcraft.jsch.*;
import com.sftptogo.util.Util;
import java.util.Properties;
import java.util.Vector;
/**
* A simple SFTP client using JSCH http://www.jcraft.com/jsch/
*/
public final class SftpClient {
private final String host;
private final int port;
private final String username;
private final JSch jsch;
private ChannelSftp channel;
private Session session;
/**
* @param host remote host
* @param port remote port
* @param username remote username
*/
public SftpClient(String host, int port, String username) {
this.host = host;
this.port = port;
this.username = username;
jsch = new JSch();
}
/**
* Use default port 22
*
* @param host remote host
* @param username username on host
*/
public SftpClient(String host, String username) {
this(host, 22, username);
}
/**
* Authenticate with remote using password
*
* @param password password of remote
* @throws JSchException If there is problem with credentials or connection
*/
public void authPassword(String password) throws JSchException {
session = jsch.getSession(username, host, port);
//disable known hosts checking
//if you want to set knows hosts file You can set with jsch.setKnownHosts("path to known hosts file");
var config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setPassword(password);
session.connect();
channel = (ChannelSftp) session.openChannel("sftp");
channel.connect();
}
public void authKey(String keyPath, String pass) throws JSchException {
jsch.addIdentity(keyPath, pass);
session = jsch.getSession(username, host, port);
//disable known hosts checking
//if you want to set knows hosts file You can set with jsch.setKnownHosts("path to known hosts file");
var config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
channel = (ChannelSftp) session.openChannel("sftp");
channel.connect();
}
/**
* List all files including directories
*
* @param remoteDir Directory on remote from which files will be listed
* @throws SftpException If there is any problem with listing files related to permissions etc
* @throws JSchException If there is any problem with connection
*/
@SuppressWarnings("unchecked")
public void listFiles(String remoteDir) throws SftpException, JSchException {
if (channel == null) {
throw new IllegalArgumentException("Connection is not available");
}
System.out.printf("Listing [%s]...%n", remoteDir);
channel.cd(remoteDir);
Vector<ChannelSftp.LsEntry> files = channel.ls(".");
for (ChannelSftp.LsEntry file : files) {
var name = file.getFilename();
var attrs = file.getAttrs();
var permissions = attrs.getPermissionsString();
var size = Util.humanReadableByteCount(attrs.getSize());
if (attrs.isDir()) {
size = "PRE";
}
System.out.printf("[%s] %s(%s)%n", permissions, name, size);
}
}
/**
* Upload a file to remote
*
* @param localPath full path of location file
* @param remotePath full path of remote file
* @throws JSchException If there is any problem with connection
* @throws SftpException If there is any problem with uploading file permissions etc
*/
public void uploadFile(String localPath, String remotePath) throws JSchException, SftpException {
System.out.printf("Uploading [%s] to [%s]...%n", localPath, remotePath);
if (channel == null) {
throw new IllegalArgumentException("Connection is not available");
}
channel.put(localPath, remotePath);
}
/**
* Download a file from remote
*
* @param remotePath full path of remote file
* @param localPath full path of where to save file locally
* @throws SftpException If there is any problem with downloading file related permissions etc
*/
public void downloadFile(String remotePath, String localPath) throws SftpException {
System.out.printf("Downloading [%s] to [%s]...%n", remotePath, localPath);
if (channel == null) {
throw new IllegalArgumentException("Connection is not available");
}
channel.get(remotePath, localPath);
}
/**
* Delete a file on remote
*
* @param remoteFile full path of remote file
* @throws SftpException If there is any problem with deleting file related to permissions etc
*/
public void delete(String remoteFile) throws SftpException {
System.out.printf("Deleting [%s]...%n", remoteFile);
if (channel == null) {
throw new IllegalArgumentException("Connection is not available");
}
channel.rm(remoteFile);
}
/**
* Disconnect from remote
*/
public void close() {
if (channel != null) {
channel.exit();
}
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
Congratulations on connecting to SFTP using Java!
Check out more code samples on Github.