AJAX file upload progress for Java using commons fileupload and prototype

This has been done before with PHP (AJAX upload progress meter for PHP) etc but I needed something a little different because I wanted to upload a file and then have it loaded into a database. I looked around and found that someone had already made something that used the commons file upload package to do the upload part (AJAX Upload progress monitor for Commons-FileUpload Example). It wasn't exactly what I was looking for but it a good start.

To understand the way this works I think it is easiest to break it down into parts:

  1. A file upload extention that counts bytes as they are uploaded
  2. An interface that monitors the progress of something running on the server
  3. AJAX to pull the monitoring into the current screen

Counting bytes when files are uploaded

This was taken from the example listed above. It extends and wraps parts of the commons File Upload classes so that you can count the bytes as they are uploaded to the server. You can download the source with build file or the binary. You will also need the commons file upload, commons io and commons logging. If you download the source put the commons jars in the lib directory before building.

The code is fairly simple to follow. MonitoredDiskFileItemFactory replaces DiskFileItemFactory and the construction of a MonitoredDiskFileItemFactory takes a OutputStreamListener that will be passed on down the chain. The new factory creates MonitoredDiskFileItems instead of DiskFileItems for each file uploaded. When the file needs to be written to disk a MonitoredOutputStream is given back instead of a normal OutputStream. The MonitoredOutputStream calls the OutputStreamListener methods as the bytes are written and with that you now have a way to monitor the byte count as the file is created on the server.

Now to test this all out we can just have an OutputStreamListener that writes its progress out to a logfile or something.

public class FileUploadListener implements OutputStreamListener
{
  private long totalFileSize;
  private long currentFileRead;

  public FileUploadListener(long totalFileSize)
  {
    this.totalFileSize = totalFileSize;
    this.currentFileRead = 0;
  }

  public void start()
  {
    log.debug("Upload started. Total file size: " + totalFileSize);
  }

  public void bytesRead(int byteCount)
  {
    log.debug("Read bytes. Currently " + byteCount + " out of " + totalFileSize + " bytes.");
    currentFileRead+=byteCount;
  }

  public void error(String error)
  {
    log.debug("Hit an error: " + error);
  }

  public void done()
  {
    log.debug("Upload done.");
  }

  public long getTotalRead()
  {
    return currentFileRead;
  }

  public long getTotalSize()
  {
    return totalFileSize;
  }
}

Now we try it out. You can put this in a servlet or jsp so I'm only going to list the parts that matter.

  FileUploadListener listener = new FileUploadListener(request.getContentLength());
  session.setAttribute("LISTENER", listener);
  FileItemFactory factory = new MonitoredDiskFileItemFactory(listener);
  ServletFileUpload upload = new ServletFileUpload(factory);
  List items = upload.parseRequest(request);
  for (Iterator i = items.iterator(); i.hasNext();)
  {
    FileItem fileItem = (FileItem) i.next();
    if (!fileItem.isFormField())
    {
       // code here to process the file
     }
   }

I'm going to assume you can find the correct way to do the actual form upload part.

Note: One issue that you will face at some point is where the upload post goes to becuase when you get to the AJAXy part of things you want the post to stay on the same page. You can use a hidden iframe and the form's "target" parameter to do this (I have an example later). This is one thing the Java examples I found didn't have but the PHP examples did and I'm not sure exactly how the Java examples work without it.

Monitoring progress on the server

The next step is to monitor the progress of the upload on the server. What you are monitoring on the server doesn't even need to be the upload. For the work I was doing the upload goes fairly quickly but what happens to the file after the upload takes a little longer. I wanted to monitor both and that is one reason I think it helps to break this up into parts because you aren't limited to just monitoring file uploads.

The main thing to keep in mind here is that the application server is multithreaded and you can make more than one request to the server at the same time. You probably know that you can open a tab in firefox or another window in ie and use the same session from the current webapp you are using. Knowing that you can create a page that monitors the status of things as they are running on the server.

From the example above you could toss the listener into the users session. Then insead of logging you just add a couple variables to keep track of the number of bytes that have been uploaded. Then create a simple jsp that pulls the Listener out of the session and dumps its data to a page. Open two windows, one to the upload page and another one to the status page. Start the upload and then start refreshing the status. You should see that the values change as the file is uploaded.

<%@page%>
<%
  FileUploadListener listener = (FileUploadListener)session.getAttribute("LISTENER");
%>
Total size: <%=listener.getTotalSize()%><br/>
Read count: <%=listener.getTotalRead()%><br/>

Of course you will probably want more than just the total size and bytes read as well as more formating like a little progress bar or something but I'll leave that up to you.

AJAX integration with prototype

You have the major parts to the upload progress done and now all you need is the AJAX part. To do this I chose to use prototype because it cuts right to what you want to do. One call is all you need to use: Ajax.PeriodicalUpdater.

The Ajax.PeriodicalUpdater call will update a container (in my case a div) on a set interval. Here is an example of how to have it update a div with an id of "status" every second.

new Ajax.PeriodicalUpdater(
                                'status',
                                'status.jsp',
                                {asynchronous:true, frequency:1, method:'get'});

The first argument is the id of the div, the second is the jsp that contains the data to stick into the div every second and the 3rd arguement is a set of options. There are more options availabe if you need them.

You would want to kick the update off whenever the form is posted. When the post is complete the iframe used as a place to post to will load with the results of the servlet or jsp that you posted to. If you return some javascript as a result for the iframe you will be able to create a final "finished" message on the page to let the user know the upload has completed and stop the processing of the AJAX updater.

So there you have it. The basics of setting up an upload progress bar using java and AJAX. I have left out a good bit but you should have enough to at least get you started.

By request I have created a simple example that pulls everything together. The source contains everything you need to create a war file including all source and an ant build file.

Example Source

Tags: , , , ,

Leave a Reply

Your email address will not be published.