Java Swing application to download files from HTTP server with progress bar
- Details
- Written by Nam Ha Minh
- Last Updated on 04 May 2023   |   Print Email
This tutorial is about how to build a Swing application that downloads a file from a HTTP server through a URL. The download application would look like this:
Its workflow is explained in this diagram:
The principal solution for this download application involves in using the java.net.HttpURLConnection class to initiates an HTTP connection with the server. The Swing client will parse HTTP headers sent from the server to determine information about the file to be downloaded such as file type, filename and content length. Then opening the HTTP connection’s input stream to read bytes transferred from the server. And an output stream is created on the client side to save the file on disk.
For more information about using HttpURLConnection class to download file, see the tutorial: Use HttpURLConnection to download file from an HTTP URL.
The following class diagram illustrates how the application will be designed:
As we can see, the application consists of the following classes:
- HTTPDownloadUtil: encapsulates the core functionality of the application - download file from an HTTP URL. This class is implemented based on the tutorial Use HttpURLConnection to download file from an HTTP URL, but is modified to work with updating download progress while the file is being transferred.
- DownloadTask: executes the file download in a background thread rather in the Swing’s event dispatcher thread (EDT), so the GUI will not become unresponsive when the download is taking place.
- SwingFileDownloadHTTP: is the main application which displays the GUI that allows users to enter a download URL, choose a location to save the file, and start downloading.
- JFilePicker and FileTypeFilter: These components are used by the SwingFileDownloadHTTP class to show a file chooser. These classes are taken from the article File picker component in Swing.
Let’s see how these classes are implemented in details.
1. Code of the HTTPDownloadUtil class
package net.codejava.swing.download; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; /** * A utility that downloads a file from a URL. * * @author www.codejava.net * */ public class HTTPDownloadUtil { private HttpURLConnection httpConn; /** * hold input stream of HttpURLConnection */ private InputStream inputStream; private String fileName; private int contentLength; /** * Downloads a file from a URL * * @param fileURL * HTTP URL of the file to be downloaded * @throws IOException */ public void downloadFile(String fileURL) throws IOException { URL url = new URL(fileURL); httpConn = (HttpURLConnection) url.openConnection(); int responseCode = httpConn.getResponseCode(); // always check HTTP response code first if (responseCode == HttpURLConnection.HTTP_OK) { String disposition = httpConn.getHeaderField("Content-Disposition"); String contentType = httpConn.getContentType(); contentLength = httpConn.getContentLength(); if (disposition != null) { // extracts file name from header field int index = disposition.indexOf("filename="); if (index > 0) { fileName = disposition.substring(index + 10, disposition.length() - 1); } } else { // extracts file name from URL fileName = fileURL.substring(fileURL.lastIndexOf("/") + 1, fileURL.length()); } // output for debugging purpose only System.out.println("Content-Type = " + contentType); System.out.println("Content-Disposition = " + disposition); System.out.println("Content-Length = " + contentLength); System.out.println("fileName = " + fileName); // opens input stream from the HTTP connection inputStream = httpConn.getInputStream(); } else { throw new IOException( "No file to download. Server replied HTTP code: " + responseCode); } } public void disconnect() throws IOException { inputStream.close(); httpConn.disconnect(); } public String getFileName() { return this.fileName; } public int getContentLength() { return this.contentLength; } public InputStream getInputStream() { return this.inputStream; } }
This utility class does not actually download the file. It only makes a connection, parses HTTP headers to get file information, and opens the connection’s input stream which will be used by the DownloadTask class.
2. Code of the DownloadTask class
package net.codejava.swing.download; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; 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 downloadURL; private String saveDirectory; private SwingFileDownloadHTTP gui; public DownloadTask(SwingFileDownloadHTTP gui, String downloadURL, String saveDirectory) { this.gui = gui; this.downloadURL = downloadURL; this.saveDirectory = saveDirectory; } /** * Executed in background thread */ @Override protected Void doInBackground() throws Exception { try { HTTPDownloadUtil util = new HTTPDownloadUtil(); util.downloadFile(downloadURL); // set file information on the GUI gui.setFileInfo(util.getFileName(), util.getContentLength()); String saveFilePath = saveDirectory + File.separator + util.getFileName(); InputStream inputStream = util.getInputStream(); // opens an output stream to save into file FileOutputStream outputStream = new FileOutputStream(saveFilePath); byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead = -1; long totalBytesRead = 0; int percentCompleted = 0; long fileSize = util.getContentLength(); while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); totalBytesRead += bytesRead; percentCompleted = (int) (totalBytesRead * 100 / fileSize); setProgress(percentCompleted); } outputStream.close(); util.disconnect(); } catch (IOException ex) { JOptionPane.showMessageDialog(gui, "Error downloading file: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); ex.printStackTrace(); setProgress(0); cancel(true); } return null; } /** * Executed in Swing's event dispatching thread */ @Override protected void done() { if (!isCancelled()) { JOptionPane.showMessageDialog(gui, "File has been downloaded successfully!", "Message", JOptionPane.INFORMATION_MESSAGE); } } }
This class implements the javax.swing.SwingWorker class so its doInBackground() method will be executed in a separate thread. It utilizes the HTTPDownloadUtil class to perform the file download, updates file information to the GUI and notifies download progress so the GUI will update the progress bar accordingly.
3. Code of the SwingFileDownloadHTTP class
package net.codejava.swing.download; 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.JOptionPane; 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 an HTTP server. * @author www.codejava.net * */ public class SwingFileDownloadHTTP extends JFrame implements PropertyChangeListener { private JLabel labelURL = new JLabel("Download URL: "); private JTextField fieldURL = new JTextField(30); private JFilePicker filePicker = new JFilePicker("Save in directory: ", "Browse..."); private JButton buttonDownload = new JButton("Download"); private JLabel labelFileName = new JLabel("File name: "); private JTextField fieldFileName = new JTextField(20); private JLabel labelFileSize = new JLabel("File size (bytes): "); private JTextField fieldFileSize = new JTextField(20); private JLabel labelProgress = new JLabel("Progress:"); private JProgressBar progressBar = new JProgressBar(0, 100); public SwingFileDownloadHTTP() { super("Swing File Download from HTTP 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); buttonDownload.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { buttonDownloadActionPerformed(event); } }); fieldFileName.setEditable(false); fieldFileSize.setEditable(false); progressBar.setPreferredSize(new Dimension(200, 30)); progressBar.setStringPainted(true); // add components to the frame constraints.gridx = 0; constraints.gridy = 0; add(labelURL, constraints); constraints.gridx = 1; constraints.fill = GridBagConstraints.HORIZONTAL; constraints.weightx = 1.0; add(fieldURL, constraints); constraints.gridx = 0; constraints.gridy = 1; constraints.weightx = 0.0; constraints.gridwidth = 2; constraints.fill = GridBagConstraints.NONE; add(filePicker, constraints); constraints.gridy = 2; constraints.anchor = GridBagConstraints.CENTER; add(buttonDownload, constraints); constraints.gridx = 0; constraints.gridy = 3; constraints.gridwidth = 1; constraints.anchor = GridBagConstraints.WEST; add(labelFileName, constraints); constraints.gridx = 1; add(fieldFileName, constraints); constraints.gridy = 4; constraints.gridx = 0; add(labelFileSize, constraints); constraints.gridx = 1; add(fieldFileSize, constraints); constraints.gridx = 0; constraints.gridy = 5; constraints.gridwidth = 1; constraints.anchor = GridBagConstraints.WEST; add(labelProgress, constraints); constraints.gridx = 1; constraints.weightx = 1.0; constraints.fill = GridBagConstraints.HORIZONTAL; add(progressBar, constraints); pack(); setLocationRelativeTo(null); // center on screen setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } /** * handle click event of the Upload button */ private void buttonDownloadActionPerformed(ActionEvent event) { String downloadURL = fieldURL.getText(); String saveDir = filePicker.getSelectedFilePath(); // validate input first if (downloadURL.equals("")) { JOptionPane.showMessageDialog(this, "Please enter download URL!", "Error", JOptionPane.ERROR_MESSAGE); fieldURL.requestFocus(); return; } if (saveDir.equals("")) { JOptionPane.showMessageDialog(this, "Please choose a directory save file!", "Error", JOptionPane.ERROR_MESSAGE); return; } try { progressBar.setValue(0); DownloadTask task = new DownloadTask(this, downloadURL, saveDir); task.addPropertyChangeListener(this); task.execute(); } catch (Exception ex) { JOptionPane.showMessageDialog(this, "Error executing upload task: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } void setFileInfo(String name, int size) { fieldFileName.setText(name); fieldFileSize.setText(String.valueOf(size)); } /** * Update the progress bar's state whenever the progress of download changes. */ @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals("progress")) { 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 SwingFileDownloadHTTP().setVisible(true); } }); } }
This builds the user interface for the application and wires all the pieces together to form a complete application. To update the progress bar while the download is taking place, it “listens” to the property named “progress” which is repeatedly updated by the DownloadTask class.
4. Testing the application
To test this application, we need to have a downloadable URL, for example:
http://www.us.apache.org/dist/struts/source/struts-2.3.12-src.zip
Run the application and type the above URL into the Download URL field:
Click Browse… button to select a directory where the file will be stored, for example: E:\Download, and hit Download button to start downloading the file. Just a second you will see the file information is updated in the fields File name and File size, as well as the progress bar gets updated quickly:
When the progress reaches to 100%, a message dialog appears:
The following error message dialog will appear in case of error such as the URL is invalid:
To experiment yourself with this nice application, download full source code and executable jar file of in the attachments section. You can also clone the sample project on GitHub.
NOTE:The application must be compiled and executed with Java 1.6 or later.
Related Tutorials:
- Use HttpURLConnection to download file from an HTTP URL
- How to create File picker component in Swing
- Swing application to download files from FTP server with progress bar
- Java Servlet Download File 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
Thanks in advance