• Nov
    • 14
    • 2011

File Downloading in JavaFX 2.0 (over HTTP)

Posted by In Uncategorized

I had to implement a File Download service for something I’m working on this week and thought I’d share.

File downloading is pretty easy these days if we use something like Apache’s HttpComponents. This can do all sorts of stuff, like proxy-support, retry-on-error and HTTP Basic Authentication. I won’t go into too much detail on this as we only need the basics here, but you can find a lot of good information about it on the web (check out their tutorial for a good starting point).

The trick to getting it working in JavaFX is the threading. Downloading a file is obviously a slow operation, the kind of thing we want to do on a background thread so we’re not blocking our GUI. Using JavaFX’s threading Worker framework we can create a fairly nifty little re-usable class for this sort of Downloading.

Have a look at the full source code for the FileDownloadTask first. As you can see, the main work happens in the ‘call’ method, so we’ll step through that in detail.

First we open a connection to our download URL. Using HttpClient this is ridiculously easy:

HttpGet httpGet = new HttpGet(this.remoteUrl);
HttpResponse response = httpClient.execute(httpGet);

There are a number of different ways we could access our response, we could get the whole thing as a String or a byte array for example, but for downloading files we want to use streams. This is as easy as:

InputStream remoteContentStream = response.getEntity().getContent();

Now we have our stream, we can use simple Java streaming to copy from our remote stream to a local file stream, but since we’re JavaFX programmers and we want to make nice, friendly GUIs for our users were going to add some support for tracking how much of the file we’ve downloaded and how much we have left to do.

First, we find out just how big our file is, using this command:

long fileSize = response.getEntity().getContentLength();

Now we can create our local file and get it ready for writing to:

File dir = localFile.getParentFile();
dir.mkdirs();
localFileStream = new FileOutputStream(localFile);

We’ll use a buffer to read chunks of data from the remote fiel and push this into our local file, as we go we’ll keep track of how many bytes we’ve downloaded:

int sizeOfChunk;
int amountComplete = 0;
while ((sizeOfChunk = remoteContentStream.read(buffer)) != -1)
{
    localFileStream.write(buffer, 0, sizeOfChunk);
    amountComplete += sizeOfChunk;
    updateProgress(amountComplete, fileSize);
}

Notice, the call to ‘updateProgress’, this is a method provided by Task that allows us to update the ‘progress; property of our task in a thread safe way. UI Controls (such as the ProgressBar) can bind to this property to monitor the progress of the download without having to do any special thread handling on their end.

That’s more or less all there is to it from the service perspective, to use this service anywhere in your code you just do the following:

FileDownloadTask fileDownloadTask = new FileDownloadTask("http://www.tagg.org/pdftest.pdf", "c:/temp/downloaded.pdf");

Generally, when you use this class there are a few tips and tricks that can be helpful.

If you want the user to choose where the download should be saved, you can use JavaFx’s FileChooser like so:

String remoteUrl = "http://media.beyondzeroemissions.org/ZCA2020_Stationary_Energy_Report_v1.pdf";
FileChooser chooser = new FileChooser();
File file = chooser.showSaveDialog(stage);
if (file != null)
{
    FileDownloadTask fileDownloadTask = new FileDownloadTask(remoteUrl, file);
    new Thread(fileDownloadTask).start();
}

To create a Progress Bar and have it display the progress of your download do the following:

ProgressBar progressBar = new ProgressBar();
progressBar.progressProperty().bind(fileDownloadTask.progressProperty());
progressBar.visibleProperty().bind(fileDownloadTask.runningProperty()); // optionally hide the progress bar when not loading

To create a launch button that is only enabled when the file is successfully download you can do this:

Hyperlink link = new Hyperlink();
link.setText(fileDownloadTask.getLocalFile().getName());
link.disableProperty().bind(fileDownloadTask.stateProperty().isNotEqualTo(Worker.State.SUCCEEDED));
link.setOnAction(new EventHandler()
{
    public void handle(ActionEvent event)
    {
        try
        {
            Desktop.getDesktop().open(fileDownloadTask.getLocalFile());
        }
        catch (IOException e)
        {
            // todo handle this by showing an error message
            e.printStackTrace();
        }
    }
});

I won’t bore you with the details of it all, but you can see a simple little demo of this all in action using this code found at: http://code.google.com/p/jfxee/source/browse/trunk/jfxee8/pom.xml

When you get it all running it should look like this:

 

8 Comments

  • Mohit
    November 24, 2011

    Great help !! thanks

  • mmommo
    September 12, 2012

    Nice job.
    Just wondering if your class supports some websites where user login are required for downloading file. Let’s say use the webengine’s session to maintain the logged in status.

    Thanks.

  • Mandar
    April 3, 2013

    Nice article!, great help!

  • April 28, 2013

    Thanks

  • August 31, 2013

    Why users still use to read news papers when in this technological world the whole thing is available on net?

  • September 3, 2013

    Great post. I was checking continuously this blog and I’m impressed!
    Very useful info specifically the last part :) I care for such
    info a lot. I was seeking this particular information for a very
    long time. Thank you and best of luck.

Leave a Comment