Google App Engine (GEA) finally got Java support. That's good. This is leaving me with no more excuses for delaying my Shogi Tools work.
Below there is a description of the process of creating a Java web application, under Eclipse environment, which uses commons FileUpload.



Do this by replacing contents of (generated by GEA plugin) index.html. Open it for editing and paste the following content:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>GAE file upload test</title>
</head>
<body>
<form name="filesForm" action="uploadtest" method="post" enctype="multipart/form-data">
File 1:<input type="file" name="file1"><br>
File 2:<input type="file" name="file2"><br>
File 3:<input type="file" name="file3"><br>
<input type="submit" name="Submit" value="Upload Files">
</form>
</body>
</html>
There are few key points here:
- form's attribute "method" is set to "post"
- form's attribute "enctype" is set to "multipart/form-data"
- form's "action" points to the servlet that will handle the upload (yet to be created)
- input filed type is "file"
Configure Apache commons in Eclipse
After unpacking the contents look for commons-fileupload-1.2.1.jar and commons-io-1.3.2.jar. Copy the files to war/WEB-INF/lib directory (see the project structure image above). This directory contains all the java libraries that will be deployed to appspot server for your app to use them.
Now, for your project to "see" them, you have to add the to the project's build path. I do this by right-clicking on the projects node in the project explorer, choosing Build Path->Configure Build Path (see the image below).



Our upload form (index.html) says it sends the data to the server with "post" method. So, in our servlet we must provide doPost method. Here is the code:
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
try {
ServletFileUpload upload = new ServletFileUpload();
upload.setSizeMax(50000);
res.setContentType("text/plain");
PrintWriter out = res.getWriter();
try {
FileItemIterator iterator = upload.getItemIterator(req);
while (iterator.hasNext()) {
FileItemStream item = iterator.next();
InputStream in = item.openStream();
if (item.isFormField()) {
out.println("Got a form field: " + item.getFieldName());
} else {
String fieldName = item.getFieldName();
String fileName = item.getName();
String contentType = item.getContentType();
out.println("--------------");
out.println("fileName = " + fileName);
out.println("field name = " + fieldName);
out.println("contentType = " + contentType);
String fileContents = null;
try {
fileContents = IOUtils.toString(in);
out.println("lenght: " + fileContents.length());
out.println(fileContents);
} finally {
IOUtils.closeQuietly(in);
}
}
}
} catch (SizeLimitExceededException e) {
out.println("You exceeded the maximu size ("
+ e.getPermittedSize() + ") of the file ("
+ e.getActualSize() + ")");
}
} catch (Exception ex) {
throw new ServletException(ex);
}
}
The code should be quite self explanatory, so I won't dwell on this (although I am open to questions :-) ).
I'll just point out few things. I marked two places in the source bold.
The first line:
upload.setSizeMax(50000);
sets the file size limit. When the user tries to upload a file larger then (in our case) 50000 bytes, an exception occurs. When setting the maximal size remember about the GAE limits and quotas.
(If you paste doPost to the class, Eclipse should automatically add imports the code uses. If not so, do add the following lines manually:
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
)
fileContents = IOUtils.toString(in);
Now you have a String containing uploaded file's data waiting for you to do whatever you want to do with it.
In my example I just output the string to the browser (so when you test it it's best to use text files).
All is set and ready. Just run the code. I do this by right-clicking on the project root node in the project browser and choosing Run As->Web Application.

So, open up your favourite browser and go to the address. Choose some files (remember the size limit) and submit the form.
In my case (I chose log4j.properties from src folder), I got the following message in my browser:
--------------
fileName = log4j.properties
field name = file2
contentType = application/octet-stream
lenght: 1063
# A default log4j configuration for log4j users.
#
# To use this configuration, deploy it into your application's WEB-INF/classes
# directory. You are also encouraged to edit it as you like.
# Configure the console as our one appender
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n
# tighten logging on the DataNucleus Categories
log4j.category.DataNucleus.JDO=WARN, A1
log4j.category.DataNucleus.Persistence=WARN, A1
log4j.category.DataNucleus.Cache=WARN, A1
log4j.category.DataNucleus.MetaData=WARN, A1
log4j.category.DataNucleus.General=WARN, A1
log4j.category.DataNucleus.Utility=WARN, A1
log4j.category.DataNucleus.Transaction=WARN, A1
log4j.category.DataNucleus.Datastore=WARN, A1
log4j.category.DataNucleus.ClassLoading=WARN, A1
log4j.category.DataNucleus.Plugin=WARN, A1
log4j.category.DataNucleus.ValueGeneration=WARN, A1
log4j.category.DataNucleus.Enhancer=WARN, A1
log4j.category.DataNucleus.SchemaTool=WARN, A1
So, everything works fine. The browser uploaded a file to our application, which now could store the file in the GAE datastore.
Hope this info is helpful to anyone.
See you next time,
fat bold cyclop
P.S. I left working example on http://100.latest.kbguesttbook.appspot.com/.