Java Swing progress bar for download process

Edward Ruchevits picture Edward Ruchevits · Aug 13, 2012 · Viewed 7k times · Source

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?

Answer

Simon Baslé picture Simon Baslé · Aug 14, 2012

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.