Java Swing application to upload files to FTP server with progress bar
- Details
- Written by Nam Ha Minh
- Last Updated on 04 May 2023   |   Print Email
This tutorial walks you through steps of developing a Swing-based application that uploads files from local computer to a remote FTP server. The application looks like this:
The following diagram describes workflow of the application:
The Swing client application connects to the server via FTP protocol to transfer files. The FTP library to be used is Apache Commons Net.
The following class diagram depicts how the application is designed:
The main classes are:
- FTPUtility: implements FTP file upload functionality.
- UploadTask: executes the file upload task in a background thread other than the Swing’s event dispatcher thread (EDT), so the GUI won’t become freezing.
- SwingFileUploadFTP: assembles user interface of the application. It allows users to specify FTP server information (host, port, username, password and upload destination), choose a file to be uploaded. It shows a progress bar while the upload is taking place.
For the classes JFilePicker and FileTypeFilter, its source code can be obtained from article File picker component in Swing. The FTPException is a custom exception class.
Let’s look at implementation of each main class in details.
1. Code of FTPUtility class
package net.codejava.swing.upload.ftp; import java.io.File; import java.io.IOException; import java.io.OutputStream; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; /** * A utility class that provides functionality for uploading files to a FTP * server. * * @author www.codejava.net * */ public class FTPUtility { private String host; private int port; private String username; private String password; private FTPClient ftpClient = new FTPClient(); private int replyCode; private OutputStream outputStream; 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()); } } /** * Start uploading a file to the server * @param uploadFile the file to be uploaded * @param destDir destination directory on the server * where the file is stored * @throws FTPException if client-server communication error occurred */ public void uploadFile(File uploadFile, String destDir) throws FTPException { try { boolean success = ftpClient.changeWorkingDirectory(destDir); if (!success) { throw new FTPException("Could not change working directory to " + destDir + ". The directory may not exist."); } success = ftpClient.setFileType(FTP.BINARY_FILE_TYPE); if (!success) { throw new FTPException("Could not set binary file type."); } outputStream = ftpClient.storeFileStream(uploadFile.getName()); } catch (IOException ex) { throw new FTPException("Error uploading file: " + ex.getMessage()); } } /** * Write an array of bytes to the output stream. */ public void writeFileBytes(byte[] bytes, int offset, int length) throws IOException { outputStream.write(bytes, offset, length); } /** * Complete the upload operation. */ public void finish() throws IOException { outputStream.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()); } } } }
This utility class is based on the FTP upload functionality described in the tutorial Upload files to a FTP server. However it is designed to allow tracking progress of the upload:
- The uploadFile() method just initiates a file transfer session with the server (opening an output stream).
- The writeFileBytes() method will be invoked by the UploadTask to transfer a byte array to the server. This helps the UploadTaskdetermines upload progress and update the progress bar appropriately.
2. Code of UploadTask class
package net.codejava.swing.upload.ftp; import java.io.File; import java.io.FileInputStream; import javax.swing.JOptionPane; import javax.swing.SwingWorker; /** * Executes the file upload in a background thread and updates progress to * listeners that implement the java.beans.PropertyChangeListener interface. * @author www.codejava.net * */ public class UploadTask 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 destDir; private File uploadFile; public UploadTask(String host, int port, String username, String password, String destDir, File uploadFile) { this.host = host; this.port = port; this.username = username; this.password = password; this.destDir = destDir; this.uploadFile = uploadFile; } /** * Executed in background thread */ @Override protected Void doInBackground() throws Exception { FTPUtility util = new FTPUtility(host, port, username, password); try { util.connect(); util.uploadFile(uploadFile, destDir); FileInputStream inputStream = new FileInputStream(uploadFile); byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead = -1; long totalBytesRead = 0; int percentCompleted = 0; long fileSize = uploadFile.length(); while ((bytesRead = inputStream.read(buffer)) != -1) { util.writeFileBytes(buffer, 0, bytesRead); totalBytesRead += bytesRead; percentCompleted = (int) (totalBytesRead * 100 / fileSize); setProgress(percentCompleted); } inputStream.close(); util.finish(); } catch (FTPException ex) { JOptionPane.showMessageDialog(null, "Error uploading 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 uploaded successfully!", "Message", JOptionPane.INFORMATION_MESSAGE); } } }
By extending the javax.swing.SwingWorker class, the UploadTask executes the file upload in a background thread (code in doInBackground() method) in order to prevent the GUI from freezing (so the GUI can update the progress bar’s state). It notifies listeners about the upload progress by calling the setProgress() method each time a byte array has been transferred. Finally the done() method is invoked when the upload is done, to display a message to the user.
3. Code of SwingFileUploadFTP class
package net.codejava.swing.upload.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 java.io.File; import javax.swing.JButton; 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 uploads files to a FTP server. * @author www.codejava.net * */ public class SwingFileUploadFTP 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 labelUploadPath = new JLabel("Upload 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 fieldUploadPath = new JTextField(30); private JFilePicker filePicker = new JFilePicker("Choose a file: ", "Browse"); private JButton buttonUpload = new JButton("Upload"); private JLabel labelProgress = new JLabel("Progress:"); private JProgressBar progressBar = new JProgressBar(0, 100); public SwingFileUploadFTP() { super("Swing File Upload to 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_OPEN); buttonUpload.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { buttonUploadActionPerformed(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(labelUploadPath, constraints); constraints.gridx = 1; add(fieldUploadPath, 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(buttonUpload, constraints); constraints.gridx = 0; constraints.gridy = 7; constraints.gridwidth = 1; constraints.anchor = GridBagConstraints.WEST; 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 Upload button */ private void buttonUploadActionPerformed(ActionEvent event) { String host = fieldHost.getText(); int port = Integer.parseInt(fieldPort.getText()); String username = fieldUsername.getText(); String password = new String(fieldPassword.getPassword()); String uploadPath = fieldUploadPath.getText(); String filePath = filePicker.getSelectedFilePath(); File uploadFile = new File(filePath); progressBar.setValue(0); UploadTask task = new UploadTask(host, port, username, password, uploadPath, uploadFile); task.addPropertyChangeListener(this); task.execute(); } /** * Update the progress bar's state whenever the progress of upload changes. */ @Override public void propertyChange(PropertyChangeEvent evt) { if ("progress" == evt.getPropertyName()) { int progress = (Integer) evt.getNewValue(); progressBar.setValue(progress); } } /** * 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 SwingFileUploadFTP().setVisible(true); } }); } }
This class constructs the user interface and is the main class of the application. It displays a form which allows the user to enter FTP server information (host name, port number, username and password) and upload information (upload path and the file to be uploaded). When the Upload button is clicked, an UploadTask object is created to execute the file upload.
It implements the method propertyChange() from the interface java.beans.PropertyChangeListener in order to be notified about the upload progress, and updates the progress bar’s state accordingly.
For simplicity, the form’s fields are not validated when the Upload button is clicked. So you should add validation code yourself.
4. Code of FTPException class
package net.codejava.swing.upload.ftp; public class FTPException extends Exception { public FTPException(String message) { super(message); } }
5. Testing the application
Run the application and enter the following information:
- Host: host name or IP address of the FTP server.
- Port: port number (default is 21 for FTP).
- Username: name of the FTP account on the server.
- Password: password of the account.
- Uploadpath: Path of the directory on the server where the file will be stored.
- Choosefile: click Browse button to pick up a file to be uploaded.
Click Upload button to start uploading the file. The progress bar is updated continuously during the file transfer:
When the upload completes, a message dialog appears:
If the username/password is incorrect, an error message dialog appears like this:
Or if the upload path does not exist on the server:
Download source code and executable jar file of this application in the attachments section. You can also clone the sample project on GitHub.
Related Tutorials:
- Java FTP File Upload Tutorial
- How to create File picker component in Swing
- Swing application to upload files to HTTP server with progress bar
- Java File Upload Servlet Example
Other Java Coding Tutorials:
- 10 Common Mistakes Every Beginner Java Programmer Makes
- 10 Java Core Best Practices Every Java Programmer Should Know
- How to become a good programmer? 13 tasks you should practice now
- How to calculate MD5 and SHA hash values in Java
- How to generate random numbers in Java
- Java File Encryption and Decryption Example
Comments
Yes, of course, because domain name will be resolved to IP address eventually.
I have an ip address instead of host name, will this code work with ip address ?
You can re-use the code that deals with FTP, but not the code for Swing GUI.