AJAX File Upload Progress for Java

AJAX File Upload Progress for Java Using Commons and XML

Most of the browser-based applications created today seem to have one thing in common: AJAX. AJAX stands for Asynchronous JavaScript and XML. It does not matter what language the page is written in. It can be PHP, JSP, ASP, or simply straight HTML. Either way, AJAX will give your web page that "Fat Client" feel that previous browser-based applications could not achieve.

This article will cover how to upload a file to your web server using the Apache Commons FileUpload package as well as how to add AJAX to the page to give the user immediate feedback as to how their upload is doing.

Figure 1: File uploading to server

Figure 2: File finished uploading

To get started, you can create a basic skeleton version of your Servlet.

package com.psclistens.ajax.fileupload;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

/**
 * This is a File Upload Servlet that is used with AJAX
 * to monitor the progress of the uploaded file. It will
 * return an XML object containing the meta information
 * as well as the percent complete.
 */
public class FileUploadServlet
   extends HttpServlet
   implements Servlet
{
   private static final long serialVersionUID = 2740693677625051632L;

   public FileUploadServlet()
   {
      super();
   }

   protected void doGet(HttpServletRequest request,
                        HttpServletResponse response)
      throws ServletException, IOException
   {
   }

   protected void doPost(HttpServletRequest request,
                         HttpServletResponse response)
      throws ServletException, IOException
   {
   }
}

Now that the skeleton Servlet is complete, create your web.xml file and add your new Servlet to it.

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" 
   xmlns="http://java.sun.com/xml/ns/j2ee" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
      http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
   <display-name>AJAXFileUploadApp</display-name>

   <servlet>
      <description>Servlet for file uploads</description>
      <display-name>File Upload Servlet</display-name>
      <servlet-name>>FileUploadServlet</servlet-name> 
      <servlet-class>
         com.psclistens.ajax.fileupload.FileUploadServlet
      </servlet-class>
   </servlet>

   <servlet-mapping>
      <servlet-name>FileUploadServlet</servlet-name>
      <url-pattern>/servlet/FileUploadServlet</url-pattern>
   </servlet-mapping>

   <welcome-file-list>
      <welcome-file>fileUpload.html</welcome-file>
   </welcome-file-list>
</web-app>

Next, you need to create a class that implements ProgressListener. ProgressListener is a class in org.apache.commons.fileupload.

package com.psclistens.ajax.fileupload;

import org.apache.commons.fileupload.ProgressListener;

/**
 * This is a File Upload Listener that is used by Apache
 * Commons File Upload to monitor the progress of the 
 * uploaded file.
 */
public class FileUploadListener 
   implements ProgressListener
{
   private volatile long 
      bytesRead = 0L,
      contentLength = 0L,
      item = 0L;   

    public FileUploadListener() 
   {
      super();
   }

   public void update(long aBytesRead, long aContentLength,
                      int anItem)
   {
      bytesRead = aBytesRead;
      contentLength = aContentLength;
      item = anItem;
   }

   public long getBytesRead() 
   {
      return bytesRead;
   }

   public long getContentLength() 
   {
      return contentLength;
   }

   public long getItem() 
   {
      return item;
   }
}

So far, so good. Now, you need to create the doPost() method of your servlet. Add the following code to the method.

   protected void doPost(HttpServletRequest request,
                         HttpServletResponse response)
      throws ServletException, IOException
   {
      // create file upload factory and upload servlet
      FileItemFactory
         factory = new DiskFileItemFactory();
      ServletFileUpload
         upload = new ServletFileUpload(factory);

      // set file upload progress listener
      FileUploadListener
         listener = new FileUploadListener();

      HttpSession
         session = request.getSession();

      session.setAttribut("LISTENER", listener);

      // upload servlet allows to set upload listener
      upload.setProgressListener(listener);

      List
         uploadedItems = null;
      FileItem
         fileItem = null;
      String
         // Path to store file on local system
         filePath = "c:\\temp";

      try
      {
         // iterate over all uploaded files
         uploadedItems = upload.parseRequest(request);

         Iterator
            i = uploadedItems.iterator();

         while (i.hasNext())
         {
            fileItem = (FileItem) i.next();

            if (fileItem.isFormField() == false)
            {
               if (fileItem.getSize() > 0)
               {
                  File
                     uploadedFile = null;
                  String
                     myFullFileName = fileItem.getName(),
                     myFileName = "",
                     slashType = (myFullFileName.lastIndexOf("\\")
                        > 0) ? "\\" : "/";    // Windows or UNIX
                  int
                     startIndex =
                        myFullFileName.lastIndexOf(slashType);

                  // Ignore the path and get the filename
                  myFileName = myFullFileName.substring
                    (startIndex + 1, myFullFileName.length());

                  // Create new File object
                  uploadedFile = new File(filePath, myFileName);

                  // Write the uploaded file to the system
                  fileItem.write(uploadedFile);
               }
            }
         }
      }
      catch (FileUploadException e)
      {
         e.printStackTrace();
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
   }

AJAX File Upload Progress for Java

You're about half way done now. The only thing you have left to do is create the HTML page and then add the AJAX portion and the Servlet to it. Start with creating the basic HTML page.

<html>
<head>
<title>Ajax File Upload</title>
</head>
<body>
   <iframe id="uploadFrameID"
           name="uploadFrame"
           height="0" width="0"
           frameborder="0"
           scrolling="yes"></iframe>
   <form id="myForm"
         enctype="multipart/form-data"
         method="post"
         target="uploadFrame"
         action="servlet/FileUploadServlet"
         onsubmit="ajaxFunction()">
   <input type="file"
          name="txtFile"
          id="txtFile" /><br />
   <input type="submit"
          id="submitID"
          name="submit"
          value="Upload" />
   </form>
</body>
</html>

The iframe is where you are going to submit your form. This will allow you to remain on the same page as the file gets uploaded to the server. This is very important because you do not want to refresh your screen.

Next, you will add a few hidden div tags that you will make visible once the form is submitted. This is how you'll update the progress of your file upload to the user.

<!-- Add hidden DIVs for updating and the progress bar
     (just a table) below the form -->
<div id="initializing"
     style="visibility: hidden; position: absolute; top: 100px;">
   <table width="100%"
          style="border: 1px; background-color: black;">
      <tr>
         <td>
            <table width="100%"
                   style="border: 1px; background-color: black;
                          color: white;">
               <tr>
                  <td align="center">
                     <b>Initializing Upload...</b>
                  </td>
               </tr>
            </table>
         </td>
      </tr>
   </table>
</div>

<div id="progressBarTable"
     style="visibility: hidden; position: absolute; top: 100px;">
   <table width="100%"
          style="border: 1px; background-color: black;
                 color: white;">
      <tr>
         <td>
            <table id="progressBar" width="0px" 
               style="border: 1px; width: 0px;
                      background-color: blue;">
                  <tr>
                     <td>&nbsp;</td>
               </tr>
            </table>
         </td>
      </tr>
   </table>
   <table width="100%"
          style="background-color: white; color: black;">
      <tr>
         <td align="center" nowrap="nowrap">
            <span id="bytesRead"
                  style="font-weight: bold;">&nbsp;</span>
         </td>
      </tr>
   </table>
</div>

<div id="percentCompleteTable" align="center"
   style="visibility: hidden; position: absolute; top: 100px;">
   <table width="100%" style="border: 1px;">
      <tr>
         <td>
            <table width="100%" style="border: 1px;">
               <tr>
                  <td align="center" nowrap="nowrap">
                     <span id="percentComplete"
                           style="color: white; font-weight:
                                  bold;">&nbsp;</span>
                  </td>
               </tr>
            </table>
         </td>
      </tr>
   </table>
</div>

You're almost done with the HTML page. However, before you add the AJAX code to the page, finish the Servlet. You now will add the contents of the doGet method. This is the method that will be called by the XMLHttpRequest object. You will set up a basic XML page and return the response back to the XMLHttpRequest.

protected void doGet(HttpServletRequest request,
                     HttpServletResponse response)
   throws ServletException, IOException
{
   PrintWriter
      out = response.getWriter();
   HttpSession
      session = request.getSession();
   FileUploadListener
      listener = null;
   StringBuffer
      buffy = new StringBuffer();
   long
      bytesRead = 0,
      contentLength = 0;

   // Make sure the session has started
   if (session == null)
   {
      return;
   }
   else if (session != null)
   {
      // Check to see if we've created the listener object yet
      listener =
         (FileUploadListener)session.getAttribute("LISTENER");

      if (listener == null)
      {
         return;
      }
      else
      {
         // Get the meta information
         bytesRead = listener.getBytesRead();
         contentLength = listener.getContentLength();
      }
   }

   response.setContentType("text/xml");

   buffy.append("<?xml version=\"1.0\"
                 encoding=\"ISO-8859-1\"?>\n");
   buffy.append("<response>\n");
   buffy.append("\t<bytes_read>" + bytesRead + "</bytes_read>\n");
   buffy.append("\t<content_length>" + contentLength +
                "</content_length>\n");

   // Check to see if we're done
   if (bytesRead == contentLength)
   {
      buffy.append("\t<finished />\n");

      // No reason to keep listener in session since we're done
      session.setAttribute("LISTENER", null);
   }
   else
   {
      // Calculate the percent complete
      long percentComplete = ((100 * bytesRead) / contentLength);

      buffy.append("\t<percent_complete>" + percentComplete +
                   "</percent_complete>\n");
   }

   buffy.append("</response>\n");

   out.println(buffy.toString());
   out.flush();
   out.close();
}

AJAX File Upload Progress for Java

Back to the HTML page. To finish this page, you need to add the most important part; AJAX. I've broken this part up into two seperate parts. The first part will return the XMLHttpRequest object. This object is initiated based on the type of browser the user is using. Internet Explorer (IE) browsers create this object differently than non-IE browsers.

Add this code to the head section of the HTML page.

<script language="javascript">
var req;

function ajaxFunction()
{
   var url = "servlet/FileUploadServlet";

   if (window.XMLHttpRequest)        // Non-IE browsers
   {
      req = new XMLHttpRequest();
      req.onReadyStateChange = processStateChange;

      try
      {
         req.open("GET", url, true);
      } 
      catch (e) 
      {
            alert(e);
      }
      req.send(null);
   }
   else if (window.ActiveXObject)    // IE Browsers
   {
      req = new ActiveXObject("Microsoft.XMLHTTP");

      if (req) 
      {
            req.onReadyStateChange = processStateChange;
            req.open("GET", url, true);
            req.send();
      }
   }
}
</script>

You may have noticed that you call a method called processStateChange during the onReadyStateChange event in the ajaxFunction method. You will implement that method below. This is the method that will parse through the XML code that is returned and update the HTML page as necessary.

Add this function just above the </script> tag.

function processStateChange()
{
   /**
   *  State    Description
   *    0      The request is not initialized
   *    1      The request has been set up
   *    2      The request has been sent
   *    3      The request is in process
   *    4      The request is complete
   */
   if (req.readyState == 4)
   {
      if (req.status == 200) // OK response
      {
         var xml = req.responseXML;

         // No need to iterate since there will only be one set
         // of lines
         var isNotFinished =
            xml.getElementsByTagName("finished")[0];
         var myBytesRead =
            xml.getElementsByTagName("bytes_read")[0];
         var myContentLength =
            xml.getElementsByTagName("content_length")[0];
         var myPercent =
            xml.getElementsByTagName("percent_complete")[0];

         // Check to see if it's even started yet
         if ((isNotFinished == null) && (myPercent == null))
         {
               document.getElementById
                  ("initializing").style.visibility = "visible";

               // Sleep then call the function again
               window.setTimeout("ajaxFunction();", 100);
            }
         else 
         {
             document.getElementById("initializing" ).
                style.visibility = "hidden";
             document.getElementById("progressBarTable" ).
                style.visibility = "visible";
             document.getElementById("percentCompleteTable" ).
                style.visibility = "visible";
             document.getElementById("bytesRead" ).
                style.visibility = "visible";

             myBytesRead = myBytesRead.firstChild.data;
             myContentLength = myContentLength.firstChild.data;

             // It's started, get the status of the upload
             if (myPercent != null)
             {
                myPercent = myPercent.firstChild.data;

                document.getElementById("progressBar").style.width =
                   myPercent + "%";
                document.getElementById("bytesRead").innerHTML =
                   myBytesRead + " of " + 
                   myContentLength + " bytes read";
                document.getElementById("percentComplete").innerHTML =
                   myPercent + "%";

                // Sleep then call the function again
                window.setTimeout("ajaxFunction();", 100);
             }
             else
             {
                document.getElementById("bytesRead").style.
                   visibility = "hidden";
                document.getElementById("progressBar").style.width =
                   "100%";
                document.getElementById("percentComplete").
                   innerHTML = "Done!";
             }
            }
      }
      else
      {
            alert(req.statusText);
      }
   }
}

Okay. That's it. You have now completed a working file upload application that uses Apache Commons FileUpload and AJAX.



About the Author

Frank Rios

Frank Rios is a Sr. Technical Consultant with PSC Group, LLC in their Kansas City office. Frank is a Sun Certified Java Programmer and has been developing and architecting Java Applications since 1998. Frank specializes in browser-based applications and also teaches Java and HTML classes. Frank has experience in Java Architecture, Analysis & Design, Full life-cycle software design, development, and implementations using Object Oriented, Web-based, and Client/Server technologies. His specialized experience includes the development of web-based, n-tiered commercial software applications utilizing Java, J2EE, Servlets, Applets, JSP, JDBC, AJAX, XML, Application Servers, and relational databases. Additional experience includes Oracle, MS SQL Server, LDAP, JavaScript, several variations of Linux, Unix. He has a Masters in Computer Science (MCS) as well as an MBA in Project Management. He is also Six Sigma Green Belt certified.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • You may already know about some of the benefits of Bluemix, IBM's open platform for developing and deploying mobile and web applications. Check out this webcast that focuses on building an Android application using the MobileData service, with a walk-through of the real process and workflow used to build and link the MobileData service within your application. Join IBM's subject matter experts as they show you the way to build a base application that will jumpstart you into building your own more complex app …

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds