Java Swing progress bar for download process

backend, frontend, java, jprogressbar, swing

Solution

when you want to do MVC in swing, the `SwingWorker` class comes to mind. SwingWorker comes with a property called "progress", that you can listen to using a `PropertyChangeListener`.

Progress events can be fired from the swingworker using its `setProgress(int 0-100)` method. So here it is for loading the file in the background with a notion of progress (note that you will need to have an idea of the size of the file to be able to compute a progress percentage).

Showing the progress can be done using two options : a `JProgressBar` for complete control, or a `ProgressMonitor` to show an almost self-managed popup with a progress bar in it. See the tutorial to see the differences.

Solution 1

As they say, if you go for a `ProgressMonitor` and your background task is reading from an `InputStream`, you can use the `ProgressMonitorInputStream` class to do the reading and displaying progress without bothering with calling setProgress or listening to the "progress" property.

Solution 2

If you want to do it manually, create your SwingWorker loading task that calls setProgress as it goes, instanciate a ProgressMonitor (or a JProgressBar) as needed, register a PropertyChangeListener on your SwingWorker that checks for "progress" changes and updates the monitor/bar accordingly.

Note: It is important to go through a PropertyChangeListener because it decouples the model (the task) from the view (the swing progress component) and abide by the EDT usage rules.

Problem

I am using Java function to download file from internet. ``` public void getLatestRelease() { try { // Function called long startTime = System.currentTimeMillis(); // Open connection System.out.println("Connecting..."); URL url = new URL(latestReleaseUrl); url.openConnection(); // Download routine InputStream reader = url.openStream(); FileOutputStream writer = new FileOutputStream("release.zip"); byte[] buffer = new byte[153600]; int totalBytesRead = 0; int bytesRead = 0; while ((bytesRead = reader.read(buffer)) > 0) { writer.write(buffer, 0, bytesRead); buffer = new byte[153600]; totalBytesRead += bytesRead; } // Download finished long endTime = System.currentTimeMillis(); // Output download information System.out.println("Done."); System.out.println((new Integer(totalBytesRead).toString()) + " bytes read."); System.out.println("It took " + (new Long(endTime - startTime).toString()) + " milliseconds."); // Close input and output streams writer.close(); reader.close(); } // Here I catch MalformedURLException and IOException :) } ``` And I have `JProgressBar` component in my `JPanel`, which is supposed to visualize download progress: ``` private static void createProgressBar(JPanel panel) { JProgressBar progressBar = new JProgressBar(0, 100); progressBar.setValue(0); progressBar.setStringPainted(true); panel.add(progressBar, BorderLayout.SOUTH); } ``` I'd like to separate "back-end" functions from "front-end" views, presented to users, by analogy with MVC in web applications. So, function `getLatestRelease()` lies in the package `framework` in class `MyFramework`. Everything, connected with `Swing` interface generation, including event listeners, is in the package `frontend`. In the main `Controller` class I create an instance of `MyFramework` and an instance of `ApplicationFrontend`, which is the main class of `frontend` package. The questions is how to update `progressBar` value, depending on download progress?

Original source