In the article Download files from a FTP server, we presented a console-based application that demonstrates how to download remote files from a FTP server. Based on that, we will go further by developing a Swing-based version of the application in this tutorial. The application looks like this:

 Swing File Download FTP application

The following information is required in order to download a remote file from the FTP server and save it on the local computer:

    • Host: Host name or IP address of the FTP server.
    • Port: FTP port number (default is 21).
    • Username: user name of an account on the FTP server.
    • Password: password of the account.
    • Download path: Full path of the file needs to be downloaded on the server, for example: /Projects/Java/FTP.zip
    • A directory on the local computer where the file will be stored.
The following diagram explains workflow of the application:

Swing File Download FTP workflow

The following class diagram shows how the application is designed:

SwingFileDownloadFTP class diagram

There are three main classes:

    • FTPUtility: implements main functions to work with the FTP server: connect, download, disconnect, etc.
    • DownloadTask: executes the file download in a background thread so that the GUI won’t become unresponsive or freezing.
    • SwingFileDownloadFTP: constructs user interface of the application which allows users to specify the information mentioned above to download the file, and updates the progress bar while the download is taking place.
Beside these main classes, the JFilePicker and FileTypeFilter classes are used to show a directory chooser dialog. Its source code can be obtained from article File picker component in Swing. The FTPException is just a simple custom exception class.



Let’s see how each main class is implemented.

 

1. Code of the FTPUtility class

package net.codejava.swing.download.ftp;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

/**
 * A utility class that provides functionality for downloading files from a FTP
 * server.
 * 
 * @author www.codejava.net
 * 
 */
public class FTPUtility {

	// FTP server information
	private String host;
	private int port;
	private String username;
	private String password;

	private FTPClient ftpClient = new FTPClient();
	private int replyCode;

	private InputStream inputStream;

	public FTPUtility(String host, int port, String user, String pass) {
		this.host = host;
		this.port = port;
		this.username = user;
		this.password = pass;
	}

	/**
	 * Connect and login to the server.
	 * 
	 * @throws FTPException
	 */
	public void connect() throws FTPException {
		try {
			ftpClient.connect(host, port);
			replyCode = ftpClient.getReplyCode();
			if (!FTPReply.isPositiveCompletion(replyCode)) {
				throw new FTPException("FTP serve refused connection.");
			}

			boolean logged = ftpClient.login(username, password);
			if (!logged) {
				// failed to login
				ftpClient.disconnect();
				throw new FTPException("Could not login to the server.");
			}

			ftpClient.enterLocalPassiveMode();

		} catch (IOException ex) {
			throw new FTPException("I/O error: " + ex.getMessage());
		}
	}

	/**
	 * Gets size (in bytes) of the file on the server.
	 * 
	 * @param filePath
	 *            Path of the file on server
	 * @return file size in bytes
	 * @throws FTPException
	 */
	public long getFileSize(String filePath) throws FTPException {
		try {
			FTPFile file = ftpClient.mlistFile(filePath);
			if (file == null) {
				throw new FTPException("The file may not exist on the server!");
			}
			return file.getSize();
		} catch (IOException ex) {
			throw new FTPException("Could not determine size of the file: "
					+ ex.getMessage());
		}
	}

	/**
	 * Start downloading a file from the server
	 * 
	 * @param downloadPath
	 *            Full path of the file on the server
	 * @throws FTPException
	 *             if client-server communication error occurred
	 */
	public void downloadFile(String downloadPath) throws FTPException {
		try {

			boolean success = ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
			if (!success) {
				throw new FTPException("Could not set binary file type.");
			}

			inputStream = ftpClient.retrieveFileStream(downloadPath);

			if (inputStream == null) {
				throw new FTPException(
						"Could not open input stream. The file may not exist on the server.");
			}
		} catch (IOException ex) {
			throw new FTPException("Error downloading file: " + ex.getMessage());
		}
	}

	/**
	 * Complete the download operation.
	 */
	public void finish() throws IOException {
		inputStream.close();
		ftpClient.completePendingCommand();
	}

	/**
	 * Log out and disconnect from the server
	 */
	public void disconnect() throws FTPException {
		if (ftpClient.isConnected()) {
			try {
				if (!ftpClient.logout()) {
					throw new FTPException("Could not log out from the server");
				}
				ftpClient.disconnect();
			} catch (IOException ex) {
				throw new FTPException("Error disconnect from the server: "
						+ ex.getMessage());
			}
		}
	}

	/**
	 * Return InputStream of the remote file on the server.
	 */
	public InputStream getInputStream() {
		return inputStream;
	}
}
The steps of method invocation to use this utility class to download a file as follows:

    • Call connect(): to connect and login to the server.
    • Call getFileSize(): to get size of file before downloading. This is needed in order to show correct progress of the download.
    • Call getInputStream(): to open input stream of the file being downloaded. The client will use the input stream to read the file’s data from the server.
    • Call finish(): to complete the file transfer session with the server.
    • Call disconnect(): to logout and close the connection with the server.  

2. Code of the DownloadTask class

package net.codejava.swing.download.ftp;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

import javax.swing.JOptionPane;
import javax.swing.SwingWorker;

/**
 * Execute file download in a background thread and update the progress.
 * 
 * @author www.codejava.net
 * 
 */
public class DownloadTask extends SwingWorker<Void, Void> {

	private static final int BUFFER_SIZE = 4096;

	private String host;
	private int port;
	private String username;
	private String password;

	private String downloadPath;
	private String saveDir;

	private SwingFileDownloadFTP gui;

	public DownloadTask(String host, int port, String username,
			String password, String downloadPath, String saveDir,
			SwingFileDownloadFTP gui) {
		this.host = host;
		this.port = port;
		this.username = username;
		this.password = password;
		this.downloadPath = downloadPath;
		this.saveDir = saveDir;
		this.gui = gui;
	}

	/**
	 * Executed in background thread
	 */
	@Override
	protected Void doInBackground() throws Exception {
		FTPUtility util = new FTPUtility(host, port, username, password);
		try {
			util.connect();

			byte[] buffer = new byte[BUFFER_SIZE];
			int bytesRead = -1;
			long totalBytesRead = 0;
			int percentCompleted = 0;

			long fileSize = util.getFileSize(downloadPath);
			gui.setFileSize(fileSize);

			String fileName = new File(downloadPath).getName();

			File downloadFile = new File(saveDir + File.separator + fileName);
			FileOutputStream outputStream = new FileOutputStream(downloadFile);

			util.downloadFile(downloadPath);
			InputStream inputStream = util.getInputStream();

			while ((bytesRead = inputStream.read(buffer)) != -1) {
				outputStream.write(buffer, 0, bytesRead);
				totalBytesRead += bytesRead;
				percentCompleted = (int) (totalBytesRead * 100 / fileSize);
				setProgress(percentCompleted);
			}

			outputStream.close();

			util.finish();
		} catch (FTPException ex) {
			JOptionPane.showMessageDialog(null,
					"Error downloading file: " + ex.getMessage(), "Error",
					JOptionPane.ERROR_MESSAGE);
			ex.printStackTrace();
			setProgress(0);
			cancel(true);
		} finally {
			util.disconnect();
		}

		return null;
	}

	/**
	 * Executed in Swing's event dispatching thread
	 */
	@Override
	protected void done() {
		if (!isCancelled()) {
			JOptionPane.showMessageDialog(null,
					"File has been downloaded successfully!", "Message",
					JOptionPane.INFORMATION_MESSAGE);
		}
	}
}
This class utilizes the FTPUtility class above to run the download in a background thread (code executed in the doInBackground() method). So the GUI won’t become freezing while the download is taking place, and the download progress is updated in the progress bar immediately. If any error occurred during the download, an error message dialog will appear. Finally, when the download completes, a successful message appears (the done() method is invoked).


3. Code of the SwingFileDownloadFTP class

package net.codejava.swing.download.ftp;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPasswordField;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import net.codejava.swing.JFilePicker;

/**
 * A Swing application that downloads file from a FTP server.
 * 
 * @author www.codejava.net
 * 
 */
public class SwingFileDownloadFTP extends JFrame implements
		PropertyChangeListener {

	private JLabel labelHost = new JLabel("Host:");
	private JLabel labelPort = new JLabel("Port:");
	private JLabel labelUsername = new JLabel("Username:");
	private JLabel labelPassword = new JLabel("Password:");
	private JLabel labelDownloadPath = new JLabel("Download path:");

	private JTextField fieldHost = new JTextField(40);
	private JTextField fieldPort = new JTextField(5);
	private JTextField fieldUsername = new JTextField(30);
	private JPasswordField fieldPassword = new JPasswordField(30);
	private JTextField fieldDownloadPath = new JTextField(30);

	private JFilePicker filePicker = new JFilePicker("Save file to: ",
			"Browse...");

	private JButton buttonDownload = new JButton("Download");

	private JLabel labelFileSize = new JLabel("File size (bytes):");
	private JTextField fieldFileSize = new JTextField(15);

	private JLabel labelProgress = new JLabel("Progress:");
	private JProgressBar progressBar = new JProgressBar(0, 100);

	public SwingFileDownloadFTP() {
		super("Swing File Download from FTP server");

		// set up layout
		setLayout(new GridBagLayout());
		GridBagConstraints constraints = new GridBagConstraints();
		constraints.anchor = GridBagConstraints.WEST;
		constraints.insets = new Insets(5, 5, 5, 5);

		// set up components
		filePicker.setMode(JFilePicker.MODE_SAVE);
		filePicker.getFileChooser().setFileSelectionMode(
				JFileChooser.DIRECTORIES_ONLY);

		fieldFileSize.setEditable(false);

		buttonDownload.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				buttonDownloadActionPerformed(event);
			}
		});

		progressBar.setPreferredSize(new Dimension(200, 30));
		progressBar.setStringPainted(true);

		// add components to the frame
		constraints.gridx = 0;
		constraints.gridy = 0;
		add(labelHost, constraints);

		constraints.gridx = 1;
		constraints.fill = GridBagConstraints.HORIZONTAL;
		constraints.weightx = 1.0;
		add(fieldHost, constraints);

		constraints.gridx = 0;
		constraints.gridy = 1;
		add(labelPort, constraints);

		constraints.gridx = 1;
		add(fieldPort, constraints);

		constraints.gridx = 0;
		constraints.gridy = 2;
		add(labelUsername, constraints);

		constraints.gridx = 1;
		add(fieldUsername, constraints);

		constraints.gridx = 0;
		constraints.gridy = 3;
		add(labelPassword, constraints);

		constraints.gridx = 1;
		add(fieldPassword, constraints);

		constraints.gridx = 0;
		constraints.gridy = 4;
		add(labelDownloadPath, constraints);

		constraints.gridx = 1;
		add(fieldDownloadPath, constraints);

		constraints.gridx = 0;
		constraints.gridwidth = 2;
		constraints.gridy = 5;
		constraints.anchor = GridBagConstraints.WEST;

		add(filePicker, constraints);

		constraints.gridx = 0;
		constraints.gridy = 6;
		constraints.anchor = GridBagConstraints.CENTER;
		constraints.fill = GridBagConstraints.NONE;
		add(buttonDownload, constraints);

		constraints.gridx = 0;
		constraints.gridy = 7;
		constraints.gridwidth = 1;
		constraints.anchor = GridBagConstraints.WEST;

		add(labelFileSize, constraints);

		constraints.gridx = 1;
		add(fieldFileSize, constraints);

		constraints.gridx = 0;
		constraints.gridy = 8;
		add(labelProgress, constraints);

		constraints.gridx = 1;
		constraints.fill = GridBagConstraints.HORIZONTAL;
		add(progressBar, constraints);

		pack();
		setLocationRelativeTo(null);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	/**
	 * handle click event of the Download button
	 */
	private void buttonDownloadActionPerformed(ActionEvent event) {
		String host = fieldHost.getText();
		int port = Integer.parseInt(fieldPort.getText());
		String username = fieldUsername.getText();
		String password = new String(fieldPassword.getPassword());
		String downloadPath = fieldDownloadPath.getText();
		String saveDir = filePicker.getSelectedFilePath();

		progressBar.setValue(0);
		DownloadTask task = new DownloadTask(host, port, username, password,
				downloadPath, saveDir, this);
		task.addPropertyChangeListener(this);
		task.execute();
	}

	/**
	 * Update the progress bar's state whenever the progress of download
	 * changes.
	 */
	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		if ("progress" == evt.getPropertyName()) {
			int progress = (Integer) evt.getNewValue();
			progressBar.setValue(progress);
		}
	}

	void setFileSize(long fileSize) {
		fieldFileSize.setText(String.valueOf(fileSize));
	}

	/**
	 * Launch the application
	 */
	public static void main(String[] args) {
		try {
			// set look and feel to system dependent
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				new SwingFileDownloadFTP().setVisible(true);
			}
		});
	}
}
This class displays the user interface and also acts as the main entry to launch the application. It wires all the pieces together to form a complete application. When the button Download is clicked, an instance of the DownloadTask class is created to carry out the download in a separate background thread other than the Swing event dispatching thread (EDT).

To get the progress bar’s state updated, this class implements the java.beans.PropertyChangeListener interface to acts as a listener of the DownloadTask. The DownloadTask class notifies progress of the upload by calling the setProgress() method.


4. Code of FTPException class

package net.codejava.swing.download.ftp;

public class FTPException extends Exception {
	public FTPException(String message) {
		super(message);
	}
}
 

5. Testing the application

Run the application by executing the SwingFileDownloadFTP class. Enter the required information and click Download button to start downloading the file:

Swing File Download FTP application

When the file is downloaded and saved completely, a successful message dialog appears like this:

Download successful message

An error message dialog will appear in case of error, e.g. incorrect username/password:

Download error message 1

Or if the download path is incorrect:

Download error message 2

You can download full source code and executable jar file for this application in the Attachments section. Note that the application requires Java 1.6 or later. The project is also available on GitHub at this repo link.

 

Related Tutorials:

 

Other Java Coding Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He started programming with Java in the time of Java 1.4 and has been falling in love with Java since then. Make friend with him on Facebook and watch his Java videos you YouTube.



Attachments:
Download this file (SwingFileDownloadFTP.zip)SwingFileDownloadFTP.zip[Full source code and executable jar file]275 kB

Add comment

   


Comments 

#5bedjik2022-09-20 02:48
Hello sir, please I wish to add a background color to your code. can you help me please
Quote
#4mosfet2015-07-18 18:32
for some reason the FTPFile.getSize() method always returns -1 for me. even when the corresponding size field is set properly:

I added these two lines to the getFileSize(String) Method:
System.out.println(file);
System.out.println(file.getSize());

output:
Type=file;Size=100000000;Modify=20150718214323.522; a.part09.rar
-1

because of this, the getFileSize Method also returns -1 and the ftp client is not able to download anything.
i have absolutely no idea whats going on with the FTPFile
Quote
#3Hòa2015-03-18 02:43
Great code !! It's really useful for me.Thanks a lot
Quote
#2Nam2014-01-08 08:13
Hi Rajesh,

There is a tutorial for that: How to download a complete folder from a FTP server (codejava.net/.../...)

You can use the ideas of this article's Swing app to build your own one to download a folder.
Quote
#1Rajesh2014-01-06 13:07
Please help me How to download files from ftp folder
Quote