2

I currently have a class that should show me a simple form while downloading a file. It is working, but the progress bar is not updating, and I can only see it once the download is finished. Can someone help me?

import java.awt.FlowLayout;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JProgressBar;

public class Downloader {

    public Downloader(String site, File file) {
        JFrame frm = new JFrame();
        JProgressBar current = new JProgressBar(0, 100);
        current.setSize(50, 100);
        current.setValue(0);
        current.setStringPainted(true);
        frm.add(current);
        frm.setVisible(true);
        frm.setLayout(new FlowLayout());
        frm.setSize(50, 100);
        frm.setDefaultCloseOperation(EXIT_ON_CLOSE);
        try {
            URL url = new URL(site);
            HttpURLConnection connection
                    = (HttpURLConnection) url.openConnection();
            int filesize = connection.getContentLength();
            float totalDataRead = 0;
            try (java.io.BufferedInputStream in = new java.io.BufferedInputStream(connection.getInputStream())) {
                java.io.FileOutputStream fos = new java.io.FileOutputStream(file);
                try (java.io.BufferedOutputStream bout = new BufferedOutputStream(fos, 1024)) {
                    byte[] data = new byte[1024];
                    int i;
                    while ((i = in.read(data, 0, 1024)) >= 0) {
                        totalDataRead = totalDataRead + i;
                        bout.write(data, 0, i);
                        float Percent = (totalDataRead * 100) / filesize;
                        current.setValue((int) Percent);
                    }
                }
            }
        } catch (IOException e) {
            javax.swing.JOptionPane.showConfirmDialog((java.awt.Component) null, e.getMessage(), "Error",
                    javax.swing.JOptionPane.DEFAULT_OPTION);
        }
    }
}
woutwoot
  • 152
  • 1
  • 12
  • Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens. Instead implement a `SwingWorker` for long running tasks. See [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/) for more details. – Andrew Thompson Feb 22 '14 at 12:55
  • 1
    And here's another dup: [JProgressBar doesn't update during download](http://stackoverflow.com/questions/17400982/jprogressbar-doesnt-update-during-download) – Hovercraft Full Of Eels Feb 22 '14 at 12:59
  • 2
    And yet a third: [My JProgressBar is not Updating Until it is 100%](http://stackoverflow.com/questions/10437590/my-jprogressbar-is-not-updating-until-it-is-100). Time to work on your Google skills! ;) – Hovercraft Full Of Eels Feb 22 '14 at 13:00
  • Thanks everyone, got it to work now. I had been searching, but nothing that I found fit what I was trying to achieve. Thanks! – woutwoot Feb 22 '14 at 16:30

1 Answers1

4

Yours is a classic Swing concurrency issue. The solution is as always -- do the long running code in a background thread such as can be found with a SwingWorker.

e.g.,

import java.awt.FlowLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutionException;

import javax.swing.JFrame;
import javax.swing.SwingWorker;

import static javax.swing.JFrame.EXIT_ON_CLOSE;

import javax.swing.JProgressBar;

public class Downloader {

   public Downloader(String site, File file) {
      JFrame frm = new JFrame();
      final JProgressBar current = new JProgressBar(0, 100);
      current.setSize(50, 100);
      current.setValue(0);
      current.setStringPainted(true);
      frm.add(current);
      frm.setVisible(true);
      frm.setLayout(new FlowLayout());
      frm.setSize(50, 100);
      frm.setDefaultCloseOperation(EXIT_ON_CLOSE);
      final Worker worker = new Worker(site, file);
      worker.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent pcEvt) {
            if ("progress".equals(pcEvt.getPropertyName())) {
               current.setValue((Integer) pcEvt.getNewValue());
            } else if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
               try {
                  worker.get();
               } catch (InterruptedException | ExecutionException e) {
                  // handle any errors here
                  e.printStackTrace(); 
               }
            }

         }
      });
      worker.execute();
   }
}

class Worker extends SwingWorker<Void, Void> {
   private String site;
   private File file;

   public Worker(String site, File file) {
      this.site = site;
      this.file = file;
   }

   @Override
   protected Void doInBackground() throws Exception {
      URL url = new URL(site);
      HttpURLConnection connection = (HttpURLConnection) url
            .openConnection();
      int filesize = connection.getContentLength();
      int totalDataRead = 0;
      try (java.io.BufferedInputStream in = new java.io.BufferedInputStream(
            connection.getInputStream())) {
         java.io.FileOutputStream fos = new java.io.FileOutputStream(file);
         try (java.io.BufferedOutputStream bout = new BufferedOutputStream(
               fos, 1024)) {
            byte[] data = new byte[1024];
            int i;
            while ((i = in.read(data, 0, 1024)) >= 0) {
               totalDataRead = totalDataRead + i;
               bout.write(data, 0, i);
               int percent = (totalDataRead * 100) / filesize;
               setProgress(percent);
            }
         }
      }
      return null;
   }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thanks for this, it works, however, it stops with an exception at 20% `java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: the value should be from 0 to 100 at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:188) at javax.swing.SwingWorker.get(SwingWorker.java:602) at Downloader$1.propertyChange(Downloader.java:49)` – woutwoot Feb 22 '14 at 16:36
  • @woutwoot: what is the value of percent when the error occurs? When calling `setProgress(int progress)` you can only pass in a value between 0 and 100. If percent is too big, say 101, consider using if blocks -- `if (percent > 100) {percent = 100)`, or some such. – Hovercraft Full Of Eels Feb 22 '14 at 16:42
  • I've just been testing, and it's only for files bigger than 20MB for some reason. The error occurs @ worker.get(); – woutwoot Feb 22 '14 at 16:54
  • @woutwoot: the `worker.get()` method will catch the exceptions thrown during the `doInBackground()` run. Try to isolate the error, and if still stuck, post a new question with your [minimal runnable example program](http://stackoverflow.com/help/mcve) and your data. – Hovercraft Full Of Eels Feb 22 '14 at 16:56
  • 1
    The reason for the error is int overflow. To fix this, you must change `totalDataRead` and `filesize` to long and then add a cast for the result of `percent`. After this change you should be able to download files up to 2GB. – Nulano Dec 30 '16 at 14:41