Earlier, we have looked at how we can copy a file from a local to a remote server using Java. This post will discuss how we can copy or transfer a directory from a local to a remote server using ssh in Java.
We will look at this from two angles-
- Transfer a directory containing only the files.
- And, Transferring a Nested directory ( A directory containing both files and directories ).
Let’s look at the above points one by one.
Transferring a directory containing only the files
Our local system directory structure looks like this –
dir
|_ _ _ file1.txt
|_ _ _ file2.txt
|_ _ _ file3.txt
|_ _ _ file4.txt
|_ _ _ file5.txt
We will copy or transfer the above directory to a remote server using the JSch library to make the ssh connection between the local and the remote server.
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
Now, we have to follow two steps –
- Make an SSH connection with the server
- Transfer all the files from one directory on the local machine to another directory on the remote server
Connecting with a remote server
We have written a different post on connecting with a server using ssh. Below is the code using which we can connect to a server.
public void connect(String username, String password, String host, int port, String FILE_KEY_NAME) {
try {
JSch jsch = new JSch();
// if there is a key, then connect using that key
// otherwise, we will connect using a password
if (FILE_KEY_NAME != null && !FILE_KEY_NAME.equals("")) {
jsch.addIdentity(FILE_KEY_NAME);
session = jsch.getSession(username, host, port);
} else {
session = jsch.getSession(username, host, port);
session.setConfig("PreferredAuthentications", "publickey,keyboard-interactive,password");
session.setPassword(password);
}
session.setConfig("StrictHostKeyChecking", "no");
session.setTimeout(15000);
session.connect();
System.out.println("Connected to: " + host);
} catch (JSchException e) {
e.printStackTrace();
session.disconnect();
}
}
- We can connect with a server by using a password or a key.
- If you have a key, you can pass the “key” file path into the connect() function, and then the key will be used to connect with the remote server.
- But if you don’t have a key, the password will be used to connect with the server.
Transferring the directory now
We also wrote a post on transferring a file from the local to a remote server. We will extend it a bit further to transfer directories as well.
We transferred the files via SFTP protocol, and SFTP in Java doesn’t provide a method to transfer the directories. So, we have to transfer all the files within the directory one by one by looping over the files in the directory.
public void transferDirectory(String pathOfLocalMachineDirectory,
String pathOfRemoteMachineDirectory) {
try {
Channel sftp = session.openChannel("sftp");
sftp.connect(15000);
ChannelSftp channelSftp = (ChannelSftp) sftp;
File[] files = new File(pathOfLocalMachineDirectory).listFiles();
for (File file : files) {
if (file.isFile()) {
String localFile = pathOfLocalMachineDirectory + file.getName();
String remoteFile = pathOfRemoteMachineDirectory+ file.getName();
channelSftp.put(localFile, remoteFile);
}
}
channelSftp.exit();
session.disconnect();
} catch (JSchException | SftpException e) {
e.printStackTrace();
session.disconnect();
}
}
- We used the listFiles() to get the files within the directory and then iterate them.
- When we encounter a file, we get that file’s name using the getName() method.
- We use the same file name and put that file in the remote server directory.
- Then we move on to the next file of the directory and repeat the same process.
The whole program to copy or transfer a directory containing just the files would look like this –
import java.io.File;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
public class Codekru {
Session session;
public static void main(String[] args) {
String remoteHostPort = "123.1.2.456"; // Your remote host ip
String username = "codekru"; // Your username
String password = "codekru"; // Your password
int port = 22; // The port (Default port is 22 )
// your local machine path of the directory
String localfilePath = "/usr/codekru/Desktop/dir";
// your remote machine path of the directory
String remoteFilePath = "/usr/workspace/remoteDir/";
Codekru codekru = new Codekru();
codekru.connect(username, password, remoteHostPort, port, null);
codekru.transferDirectory(localfilePath, remoteFilePath);
}
public void connect(String username, String password, String host, int port, String FILE_KEY_NAME) {
try {
JSch jsch = new JSch();
// if there is a key, then connect using that key
// otherwise, we will connect using a password
if (FILE_KEY_NAME != null && !FILE_KEY_NAME.equals("")) {
jsch.addIdentity(FILE_KEY_NAME);
session = jsch.getSession(username, host, port);
} else {
session = jsch.getSession(username, host, port);
session.setConfig("PreferredAuthentications", "publickey,keyboard-interactive,password");
session.setPassword(password);
}
session.setConfig("StrictHostKeyChecking", "no");
session.setTimeout(15000);
session.connect();
System.out.println("Connected to: " + host);
} catch (JSchException e) {
e.printStackTrace();
session.disconnect();
}
}
public void transferDirectory(String pathOfLocalMachineDirectory,
String pathOfRemoteMachineDirectory) {
try {
Channel sftp = session.openChannel("sftp");
sftp.connect(15000);
ChannelSftp channelSftp = (ChannelSftp) sftp;
File[] files = new File(pathOfLocalMachineDirectory).listFiles();
for (File file : files) {
if (file.isFile()) {
String localFile = pathOfLocalMachineDirectory + file.getName();
String remoteFile = pathOfRemoteMachineDirectory+ file.getName();
channelSftp.put(localFile, remoteFile);
}
}
channelSftp.exit();
session.disconnect();
} catch (JSchException | SftpException e) {
e.printStackTrace();
session.disconnect();
}
}
}
Let’s now see how we can transfer a directory containing both files and directories.
Transferring nested directories
Nested directories contain directories within directories along with the files. So, our directory structure would be like this –
dir1
|_ _ _ file1.txt
|_ _ _ file2.txt
|_ _ _ file3.txt
|_ _ _ dir2
|_ _ _ abc1.html
|_ _ _ abc2.html
|_ _ _ dir3
|_ _ _ test1.txt
|_ _ _ test2.txt
- Here, dir1 contains some files and dir2.
- Then dir2 contains some files and dir3.
- And dir3 contains only the files.
Now, our old logic worked only for the directories that contain files. But what should we do when encountering a directory within a directory?
We would have to create the nested directory( say, dir2 ) in the remote server first, and then we can transfer the files of that nested directory.
How to make a directory on the remote machine?
We would only have to run the “mkdir” command on the remote machine. You can look at this article to learn how to execute commands on the remote machine.
Below is the function that can execute the commands on the remote machine –
private String executeCommand(String command) {
String finalResult = "";
try {
String result = null;
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.setInputStream(null);// this method should be called before connect()
((ChannelExec) channel).setErrStream(System.err);
InputStream inputStream = channel.getInputStream();
channel.connect();
byte[] byteObject = new byte[10240];
while (true) {
while (inputStream.available() > 0) {
int readByte = inputStream.read(byteObject, 0, 1024);
if (readByte < 0)
break;
result = new String(byteObject, 0, readByte);
finalResult = finalResult + result;
}
if (channel.isClosed())
break;
}
channel.disconnect();
System.out.println("Disconnected channel " + channel.getExitStatus());
} catch (Exception e) {
e.printStackTrace();
}
return finalResult;
}
We only have to call the executeCommand() function with the “mkdir” command.
Let’s make a function that helps create the remote machine’s directory.
public void makeDirectoryOnRemoteMachine(String remoteDirectoryPath) {
String mkdirCommand = "mkdir -p '" + remoteDirectoryPath+"'";
executeCommand(mkdirCommand);
}
“-p” in the mkdir command is used to make the parent directories if they don’t exist.
Now, we will have to add the below code to iterate on the nested directories recursively while making the nested directory on the remote machine.
else if (file.isDirectory()) {
String localDirectoryPath = pathOfLocalMachineDirectory + file.getName() + "/";
String remoteDirectoryPath = pathOfRemoteMachineDirectory + file.getName() + "/";
makeDirectoryOnRemoteMachine(remoteDirectoryPath); // making the directory on the remote machine
transferNestedDirectory(localDirectoryPath, remoteDirectoryPath); // recursively calling the function
}
So, the overall class would look like this –
import java.io.File;
import java.io.InputStream;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
public class Codekru {
Session session;
public static void main(String[] args) {
String remoteHostPort = "123.1.2.456"; // Your remote host ip
String username = "codekru"; // Your username
String password = "codekru"; // Your password
int port = 22; // The port (Default port is 22 )
// your local machine path of the directory
String localfilePath = "/usr/codekru/Desktop/dir";
// your remote machine path of the directory
String remoteFilePath = "/usr/workspace/remoteDir/";
Codekru codekru = new Codekru();
codekru.connect(username, password, remoteHostPort, port, null);
codekru.transferNestedDirectory(localfilePath, remoteFilePath);
codekru.session.disconnect();
}
public void connect(String username, String password, String host, int port, String FILE_KEY_NAME) {
try {
JSch jsch = new JSch();
// if there is a key, then connect using that key
// otherwise, we will connect using a password
if (FILE_KEY_NAME != null && !FILE_KEY_NAME.equals("")) {
jsch.addIdentity(FILE_KEY_NAME);
session = jsch.getSession(username, host, port);
} else {
session = jsch.getSession(username, host, port);
session.setConfig("PreferredAuthentications", "publickey,keyboard-interactive,password");
session.setPassword(password);
}
session.setConfig("StrictHostKeyChecking", "no");
session.setTimeout(15000);
session.connect();
System.out.println("Connected to: " + host);
} catch (JSchException e) {
e.printStackTrace();
session.disconnect();
}
}
public void transferNestedDirectory(String pathOfLocalMachineDirectory, String pathOfRemoteMachineDirectory) {
try {
Channel sftp = session.openChannel("sftp");
sftp.connect(15000);
ChannelSftp channelSftp = (ChannelSftp) sftp;
File[] files = new File(pathOfLocalMachineDirectory).listFiles();
for (File file : files) {
System.out.println("Dir Name: " + pathOfRemoteMachineDirectory + ", fileName: " + file.getName());
if (file.isFile()) {
String localFile = pathOfLocalMachineDirectory + file.getName();
String remoteFile = pathOfRemoteMachineDirectory + file.getName();
channelSftp.put(localFile, remoteFile);
} else if (file.isDirectory()) {
String localDirectoryPath = pathOfLocalMachineDirectory + file.getName() + "/";
String remoteDirectoryPath = pathOfRemoteMachineDirectory + file.getName() + "/";
makeDirectoryOnRemoteMachine(remoteDirectoryPath);
// iterate recursively on the nested directory
transferNestedDirectory(localDirectoryPath, remoteDirectoryPath);
}
}
// session.disconnect();
} catch (JSchException | SftpException e) {
e.printStackTrace();
session.disconnect();
}
}
public void makeDirectoryOnRemoteMachine(String remoteDirectoryPath) {
String mkdirCommand = "mkdir -p '" + remoteDirectoryPath + "'";
executeCommand(mkdirCommand);
}
private String executeCommand(String command) {
String finalResult = "";
try {
String result = null;
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.setInputStream(null);// this method should be called before connect()
((ChannelExec) channel).setErrStream(System.err);
InputStream inputStream = channel.getInputStream();
channel.connect();
byte[] byteObject = new byte[10240];
while (true) {
while (inputStream.available() > 0) {
int readByte = inputStream.read(byteObject, 0, 1024);
if (readByte < 0)
break;
result = new String(byteObject, 0, readByte);
finalResult = finalResult + result;
}
if (channel.isClosed())
break;
}
} catch (Exception e) {
e.printStackTrace();
session.disconnect();
}
return finalResult;
}
}
This will transfer the directory from the local machine to a remote server.
We hope that you have liked the article. If you have any doubts or concerns, please feel free to write us in the comments or mail us at admin@codekru.com.