Date: 02/24/07
Ajax File Uplaod Example Using Java Web Parts (and Commons Fileupload)
ajax-upload.zip source code
Ajax File Upload Example Using Prototype Javascript (and Commons Fileupload)
ajax-upload2.zip source code
Introduction
Above are two simple examples of using Ajax and Servlets to create an upload progress bar with upload status
information. One uses Java Web Parts,
the other Prototype Javascript They both rely on using commons-fileupload, commons-io, and a smaller package called fileupload-ext.
(All of these are included with the application. The source code for fileupload-ext can be obtained here
http://www.ioncannon.net/examples/fileupload-ext.zip.)
The majority of the Prototype Javscript File Upload implementaion was taken from the example
found here: http://www.ioncannon.net/java/38/ajax-file-upload-progress-for-java-using-commons-fileupload-and-prototype/.
I cut a few things out and added a few changes here and there. Both the JWP version and the Protytpe version use practical identical servlets. The only major
difference between the two examples is that when using Java Web Parts (JWP) you create a config xml file to
define your ajax event and then
use tags within the jsp. In contrast the Prototype Javascript Example uses a javscript file and you have to code some
small pieces of javascript for your hooks. The main differences between the two versions below are highlighted in red.
Requirements and Building
Java5 is required as well as an applciation server such as Tomcat 5.x that implements the Servlet 2.4 and JavaServer Pages 2.0 specifications. Ant is used for the builds. Makse sure you modify the webapp/WEB-INF/app.properties file and declare the directory where you want to upload files to. Running 'ant build' will
create a war and explode the application in the ajax-upload/dist directory. Move this war to your jboss server instance and start jboss. Then go to http://localhost:8080/ajax-upload or http://localhost:8080/ajax-upload2 and select a file from your system to upload. It helps to choose a very large file to really see the upload process.
index.jsp
index.jsp for JWP version
<%@ taglib prefix="ajax" uri="javawebparts/ajaxparts/taglib" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% session.removeAttribute("FILE_UPLOAD_STATS"); %>
<html>
<head>
<title>File Upload Ajax Example </title>
<script src="script/main.js" type="text/javascript"> </script>
<link rel="stylesheet"
href="/css/main.css" type="text/css">
{ ... see src code for some simple javascript/css removed here for space ... }
<script>
function process() {
toggleButton('submit_button');
startpage_checkUploadStatus();
return true;
}
</script>
</head>
<body>
<h1>File Upload </h1>
<!-- This iframe is used as a place for the post to load -->
<iframe id='target_upload' name='target_upload' src='' style='display: none'> </iframe>
<div id="messageDiv"> </div>
<br/>
<ajax:timer ajaxRef="page/checkUploadStatus" startOnLoad="false" frequency="700" />
<div id="contentDiv" style="display:block">
<form enctype="multipart/form-data" name="fileUploadForm"
action="FileUpload" method="post" target="target_upload"
target="target_upload" onSubmit="process();">
Choose File To Upload: <input type="file" name="datafile" size="60" >
<br/> <br/>
<input type="hidden" name="doAjaxStatus" value="true"/>
<input type="submit" id="submit_button" value="submit">
</form>
</div>
</body>
</html><ajax:enable/>
index.jsp for Prototype version
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% session.removeAttribute("FILE_UPLOAD_STATS"); %>
<html>
<head>
<title>File Upload Exmaple Using Prototype Javascript </title>
<script src="script/prototype.js" language="JavaScript" type="text/javascript"> </script>
<link rel="stylesheet" href="css/main.css" type="text/css">
<script type="text/javascript" language="JavaScript">
var updater = null;
function startStatusCheck() {
document.getElementById("messageDiv").innerHTML = "";
$('submitButton').disabled = true;
updater = new Ajax.PeriodicalUpdater(
'messageDiv',
'UploadStatus',
{asynchronous:true, frequency:1, method: 'post', onFailure: reportError});
return true;
}
function reportError(request) {
killUpdate("Error communicating with server. Please try again.");
}
function killUpdate() {
$('submitButton').disabled = false;
updater.stop();
new Ajax.Updater('messageDiv',
'UploadStatus',
{asynchronous:true, method: 'post', parameters: 'status=end', onFailure: reportError});
}
</script>
{ ... see src code for some simple css removed here for space ... }
</head>
<body>
<h1>File Upload </h1>
<!-- This iframe is used as a place for the post to load -->
<iframe id='target_upload' name='target_upload' src='' style='display: none'> </iframe>
<div id="messageDiv"> </div>
<br/> <br/>
<div id="contentDiv" style="display:block">
<form enctype="multipart/form-data" name="fileUploadForm" action="FileUpload"
method="post"
target="target_upload"
onsubmit="return startStatusCheck();">
Choose File To Upload: <input type="file" name="datafile" size="60">
<br/> <br/>
<input id="submitButton" type="submit" value="submit">
</form>
</div>
</body>
</html>
FileUploadServlet
FileUploadServlet for JWP Version
{imports removed}
public class FileUploadServlet extends HttpServlet {
public void init(ServletConfig config) throws ServletException {
super.init(config);
String propsFilePath = "/WEB-INF/app.properties";
try {
Properties properties = new Properties();
InputStream inputStream = config.getServletContext().getResourceAsStream(propsFilePath);
properties.load(inputStream);
inputStream.close();
getServletContext().setAttribute("properties", properties);
System.out.println("loaded properties file into servletContext as 'properties'");
} catch (Exception e) {
throw new ServletException("Error loading properties file from path "+propsFilePath, e);
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String saveFilePath = ((Properties) getServletContext().getAttribute("properties")).getProperty("save-file-path");
FileUploadListener listener = new FileUploadListener(request.getContentLength());
request.getSession().setAttribute("FILE_UPLOAD_STATS", listener.getFileUploadStats());
DiskFileItemFactory factory = new MonitoredDiskFileItemFactory(listener);
factory.setRepository(new File(saveFilePath));
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()) {
fileItem.write(new File(saveFilePath + fileItem.getName()));
}
}
System.out.println("Files finished being written to file system at "+saveFilePath );
} catch (Exception e) {
//to be safe put error in stats so that our ajax progress can pick this up
FileUploadStats stats = new FileUploadListener.FileUploadStats();
stats.setCurrentStatus(FileUploadStatus.ERROR);
request.getSession().setAttribute("FILE_UPLOAD_STATS", stats);
e.printStackTrace();
}
}
}
FileUploadServlet for Prototype Version
The FileUploadServlet is IDENTICAL to the above, except for that at the very end of our doPost we return a stream
that will execute some javascript:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
...as above example, then after the catch:
response.getOutputStream().print(" <html> <head> <script type='text/javascript'>function killUpdate()");
response.getOutputStream().print("{ window.parent.killUpdate(); } </script> </head>");
response.getOutputStream().print(" <body onload='killUpdate();'> </body> </html>");
}
AjaxUploadStatusServlet
AjaxUploadStatusServlet for JWP Version
{imports left out}
public class AjaxUploadStatusServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("doPost of AjaxUploadStatusServlet");
FileUploadStats stats = (FileUploadStats) request.getSession().getAttribute("FILE_UPLOAD_STATS");
if (stats != null) {
response.addHeader("Cache-Control", "no-store, no-cache, must-revalidate");
response.addHeader("Cache-Control", "post-check=0, pre-check=0");
response.addHeader("Pragma", "no-cache");
double percentComplete = stats != null ? percentComplete = stats.getPercentComplete() : 0.0;
long bytesProcessed = stats.getBytesRead();
long sizeTotal = stats.getTotalSize();
DecimalFormat decFormatter = new DecimalFormat(".00");
long elapsedTimeInMilliseconds = stats.getElapsedTimeInMilliseconds();
double bytesPerMillisecond = bytesProcessed / (elapsedTimeInMilliseconds + 0.00001);
long estimatedMillisecondsLeft = (long)((sizeTotal - bytesProcessed) / (bytesPerMillisecond + 0.00001));
String timeLeft = null;
if ( ( estimatedMillisecondsLeft/3600 ) > 24 ) {
timeLeft = (long)(estimatedMillisecondsLeft/3600) + "hours";
} else {
Calendar c = new GregorianCalendar();
long ad = estimatedMillisecondsLeft - (c.get(Calendar.ZONE_OFFSET)+c.get(Calendar.DST_OFFSET));
timeLeft = new SimpleDateFormat("HH:mm:ss").format(ad);
}
PrintWriter out = response.getWriter();
out.print(" <div class='prog-border'> <div class='prog-bar' style='width: " + (percentComplete * 100) +
"%;'> </div> </div>");
out.print(" <table> <tr> <td class='st' width='250px;'>"+timeLeft+
" (at " + decFormatter.format(bytesPerMillisecond) + "kb/sec) </td> <td class='st'>"+
decFormatter.format((double)bytesProcessed/(1000*1000)) +
" MB / "+decFormatter.format((double)sizeTotal/(1000*1000))+" MB ( "+
Math.ceil((percentComplete * 100))+"% ) </td> </tr> </table>");
if (stats.getCurrentStatus() == FileUploadStatus.DONE ||stats.getCurrentStatus() == FileUploadStatus.ERROR) {
out.print( stats.getCurrentStatus() == FileUploadStatus.DONE ? " <b>Upload Complete! </b>" : " <b>Error! </b>");
out.println(" <script>");
out.println("setTimeout(\"stoppage_checkUploadStatus()\", 500);toggleButton('submit_button');");
out.println(" </script>");
}
out.flush();
}
}
}
AjaxUploadStatusServlet for Rrototype Version
The Prototype Javascript version is identical to the above Servlet except the ending javascript (in red) is not
needed in the Prototype version. (The Prototype signals its finished by the response created in the FileUploadServlet.)
Other Differences
JWP version
The JWP version required the two JWP jars to be included in the WEB-INF/lib and requires an ajax-config.xml file to be
placed in your WEB-INF directory. The file in this example application looks like:
<ajaxConfig>
<group ajaxRef="page">
<element ajaxRef="checkUploadStatus">
<event type="timer">
<requestHandler type="std:SimpleRequest" method="post">
<target>/UploadStatus </target>
<parameter/>
</requestHandler>
<responseHandler type="std:InnerHTML">
<parameter>messageDiv </parameter>
</responseHandler>
</event>
</element>
</group>
</ajaxConfig>
The JWP Version also requires you to add this to your web.xml file (placed right before the servlet definitions):
<context-param>
<param-name>AjaxPartsTaglibConfig </param-name>
<param-value>/WEB-INF/ajax_config.xml </param-value>
</context-param>
Prototype Javascript version
The Prototype version does not need the above jars, ajax-config.xml, or modification to the web.xml. It does however
require the prototype.js file.
Code and Lesson - Rick Reumann
|