Home > JAVA > [Java-Tip] Non-Blocking Method To Download Files From Web

[Java-Tip] Non-Blocking Method To Download Files From Web

November 9th, 2010

java-150x150
The URLConnection class contains many methods that let you communicate with the URL over the network. But the URLConnection doesn’t provide a callback mechanism to know the data read progress. Java’s support of interfaces provides a mechanism by which we can get the equivalent of callbacks. The trick is to define a simple interface that declares the method we wish to be invoked and to notify the data read progress of a URLConnection.

We define our Event as follows.

// FileDownloadEvent.java
 
public interface FileDownloadEvent
{
    // This is just a regular method so it can return something or
    // take arguments if you like.
    public void dataReadProgress (int done, int total, byte data[]);
    public void done(boolean error);
}

There is two methods, the dataReadProgress() and thedone() method. We invoke the dataReadProgress() each time we read a chunk of data to notify the data read progress. We use the done() method to inform the data read is over or an error has happened.

The class that will signal the event needs to expect objects that implement the dataReadProgress interface and then invoke the dataReadProgress() method as appropriate.
We will keep a counter for the downloaded data and fire the dataReadProgress event each time we read a chunk of data.

// FileDownload.java
 
import java.net.URL;
import java.net.URLConnection;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.BufferedInputStream;
import java.util.Arrays;
import java.lang.Thread;
import java.lang.Runnable;
 
public class FileDownload extends Thread implements Runnable
{
    private FileDownloadEvent ie;
    private InputStream is = null;
    private DataInputStream dis = null;
    private int dataReadSize = 4096;
    private String downloadURL = null;
 
    public FileDownload (FileDownloadEvent event)
    {
        // Save the event object for later use.
        ie = event;
    }
 
    public void request (String url)
    {
        this.downloadURL = url;
	this.start();
    }
 
    //...
    public void run ()
    {
        boolean error = false;
        try {
            URL url = new URL(this.downloadURL);
	    URLConnection fdCon = url.openConnection();
 
            int total = fdCon.getContentLength();
 
            is = url.openStream();  // throws an IOException
            dis = new DataInputStream(new BufferedInputStream(is));
 
	    byte[] data = new byte[dataReadSize];
	    int progress = 0, n;
            while ((n = dis.read(data)) > 0) {
	        progress += n;
                this.ie.dataReadProgress (progress, total, data);
                Arrays.fill(data, (byte)0);
            }
        } catch (Exception e)
        {
            error = true;
        }
        this.ie.done(error);
    }
    // ...
}

The code that wishes to receive the event notification must implement the FileDownloadEvent interface and just pass a reference to itself to the event notifier or do as in the below code.

// Download.java
 
public class Download
{
    public static void main(String args[])
    {
        // Create the event notifier and pass ourself to it.
        FileDownload req = new FileDownload (new FileDownloadEvent() {
            // Define the actual handler for the event.
            public void dataReadProgress (int done, int total, byte[] data)
            {
                System.out.println("Progress: " + ((float)done/(float)total) * 100 + "%");
                // Do something with data...
            }
            public void done (boolean error)
            {
		System.out.println("Download Completed.");
                // Do something...
            }
        });
 
        req.request("http://somedomain/path/to/file.gz");
 
	// Do something
    }
}

That’s all there is to it. I hope use this simple Java idiom will be useful to someone.

Categories: JAVA Tags: , , ,
Comments are closed.