Monitoring download/upload progress using JSch library

5 minute read

In this post, I’m going to show you how to monitor progress of download/upload using JSch library in Java. Let’s see how to do it step by step:

1. Establishing SSH connection

Before using SFTP, first we need to establish SSH connection to the remote host. Following code demonstrates that how we can do this using JSch. (Of course, following codes don’t have any concern of following good coding practices. So, when you implement your code, please consider making your methods more modular, parameterized etc.)

 1    public Session connectSession() throws JSchException {
 2        JSch jsch = new JSch();
 3        Properties config = new Properties();
 4        // Disable StrictHostKeyChecking to directly connect using username and
 5        // password, it's not a good practice for security concerns, not
 6        // recommended when you are not working in internal network etc.
 7        config.put("StrictHostKeyChecking", "no");
 8        Session session = jsch.getSession("username", "host", 22);
 9        session.setConfig(config);
10        session.setPassword("password");
11        session.connect();
12        return session;
13    }

2. Opening SFTP channel using session

Now, we have SSH session and we can open a SFTP channel. It’s very simple and can be seen from following code:

1    public ChannelSftp connectSftpChannel(Session session) throws JSchException {
2        ChannelSftp sftpChannel = (ChannelSftp) session.openChannel("sftp");
3        sftpChannel.connect();
4        return sftpChannel;
5    }

3. Implementing our SftpProgressMonitor class

If we look at ChannelSftp(provided by JSch library) class’ source code, it provides bunch of overloaded get(…) and put(…) methods.

get(…) method is used to download a file, and put(…) method is used to upload a file.

Let’s look at following method signatures from ChannelSftp class:

1    public void get(String src, String dst, SftpProgressMonitor monitor, int mode) throws SftpException;
2    
3    public void put(String src, String dst, SftpProgressMonitor monitor, int mode) throws SftpException;

(For rest of the post, when I mention get(…) or put(…), I’ll mean the above method signatures)

I think you noticed that there is a parameter of type SftpProgressMonitor. SftpProgressMonitor is an interface provided by JSch. Library says that “Hey, give me your implementation of this interface and I will use its methods inside get(…) and put(…) methods.” So, let’s look at SftpProgressMonitor interface:

1    public interface SftpProgressMonitor{
2        public static final int PUT=0;
3        public static final int GET=1;
4        public static final long UNKNOWN_SIZE = -1L;
5        void init(int op, String src, String dest, long max);
6        boolean count(long count);
7        void end();
8    }

So, you need to implement init, count and end methods in your own implementation. But what do these methods mean actually? When these methods are executed?

For example, assume that you want to download a 100 MB file from remote host and you run get(…) method.

Assume that we called the get(…) method like this:

1    sftpChannel.get("/source/test.txt", "/target/test.txt", new MySftpProgressMonitor(), ChannelSftp.RESUME);

Let’s trace what’s happening in methods by simplifying: (You can think this as debugging.)

 1    // When we call get(...) method with above parameters, monitor object's
 2    // methods will be called like this:
 3    
 4    // init method will be called at the beginning of the get(...) method
 5    monitor.init(SftpProgressMonitor.GET, "/source/test.txt", "/target/test.txt", 104857600); // 104857600 is the size in bytes of the source file which is 100 MB
 6    
 7    // count method will be called at each iteration during file transfer, the
 8    // given parameter is the byte size is transfered in an iteration
 9    // if the mode is RESUME then get method will check the dest file is already
10    // there and if it is, then the count method will be called with already
11    // transferred bytes at the beginning, and continues to increment at each
12    // iteration
13    monitor.count(10000); // 10000 is a fake number, don't focus on it
14    
15    // end method will be called when the transfer is finished
16    monitor.end();

Now, we’ve seen a lot of things and we’re ready to implement our SftpProgressMonitor. I hope following implementation gives you an inspiration about how to implement your own:

 1    public class MySftpProgressMonitor implements SftpProgressMonitor {
 2        private long totalBytes;
 3        private long transferredBytes;
 4        private NumberFormat formatter = new DecimalFormat("#0.00");
 5    
 6        public MySftpProgressMonitor(){
 7    
 8        }
 9    
10        @Override
11        public void init(int op, String src, String dest, long max) {
12            totalBytes = max;
13            System.out.println("Sftp Progress monitor is initialized for op " + op
14                                + " src: " + src + " dest: " + dest + " max: " + max);
15        }
16    
17        @Override
18        public boolean count(long count) {
19            transferredBytes += count;
20            double progress = (double) transferredBytes/totalBytes;
21            System.out.println("Current progress is: %" + formatter.format(progress*100));
22            return true;
23        }
24    
25        @Override
26        public void end() {
27            System.out.println("Sftp Progress monitor is ended.");
28        }
29    }

Well, the above code is okay and functional but you may realized that printing in count() method is not the best solution because the count() method can be excessively called in get(…) method. So, the following implemantation would be a better solution:

 1public class MySftpProgressMonitor implements SftpProgressMonitor {
 2        private long totalBytes;
 3        private long transferredBytes;
 4        private double progress;
 5    
 6        public MySftpProgressMonitor(){
 7    
 8        }
 9    
10        @Override
11        public void init(int op, String src, String dest, long max) {
12            totalBytes = max;
13        }
14    
15        @Override
16        public boolean count(long count) {
17            transferredBytes += count;
18            progress = (double) transferredBytes/totalBytes;
19            return true;
20        }
21    
22        // Reset the values, so the same MySftpProgressMonitor object can be reused
23        // within same thread
24        @Override
25        public void end() {
26            totalBytes = 0;
27            transferredBytes = 0;
28            progress = 0;
29        }
30    
31        // Provide getters, so you can retrieve the information only when it is
32        // needed, now this class is only responsible for tracking information not
33        // for printing/logging etc.
34        public long getTotalBytes(){
35            return totalBytes;
36        }
37    
38        public long getTransferredBytes(){
39            return getTransferredBytes;
40        }
41    
42        public double getProgress(){
43            return progress;
44        }
45    }

This is the end of the post, hope you enjoyed! Please reach me if you have any questions, suggestions etc.