Engineering Full Stack Apps with Java and JavaScript
Java EE introduced built in support for handling multipart MIME file uploads in Servlet 3.0 (Java EE 6). If you enable this functionality on a servlet, the container will make additional methods available on an HttpServletRequest to get all parts available on a request and also to get one of those parts passing in its name. We will see a demo of the multipart file upload in Servlet 3.0 using multipart config.
For theory and explanations on APIs, please refer to http://javajee.com/multipart-file-upload-in-servlet-30-using-multipart-config.
You may want to package the application as a war, deploy it into tomcat and then start tomcat manually following steps mentioned @ http://javajee.com/packaging-and-deploying-a-web-application-into-standalone-apache-tomcat as you may find issues in finding the real path to save file when directly working from eclipse integrated with tomcat.
HTML Form
We will need an html form to upload the file from client side. To send the request as multipart/form-data, the enctype attribute must be supplied and set to multipart/form-data, so that no characters are encoded. Standard way of writing file to a disk is using a FileOutputStream. However the new Part interface also provides a method to write. So we will also have a radio button asking client on which method to use: Part.write or using FileOutputStream.
<!DOCTYPE html>
<html lang="en">
<head>
<title>File Upload</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<form method="POST" action="upload" enctype="multipart/form-data">
File:
<input type="file" name="file" id="file" />
<br />
<br />
<input type="submit" value="Upload" name="upload" id="upload" />
<br/>
</br>
</br>
Write Options:
<br/> <input type="radio" name="writemethod" value="partwrite">Using Part.write()</input>
<br/> <input type="radio" name="writemethod" value="fileoutputstream">Creating FileOutputStream</input>
</form>
</body>
</html>
The Servlet
You need a servlet that has the url pattern “upload”. Out servlet will ask a method processRequest to process this request passing the request and response object. This method will first retrieve the part for the file parameter. We will then calculate the filename from the part. Based on the radio button value for write options, we will decide whether to write using Part’s write method or using the standard way of FileOutputStream. We will also capture the time elapsed for writing.
FileUploadServlet.java
package com.javajee.servlets;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import org.apache.catalina.connector.Request;
@WebServlet(name = "FileUploadServlet", urlPatterns = { "/upload" })
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String uploadFolderName = "uploads";
private static volatile String uploadFolder = null;
public FileUploadServlet() {
super();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
final PrintWriter out = response.getWriter();
try {
processRequest(request, response);
} catch (IOException e) {
out.print("<br>ERROR! " + e.getMessage());
}
}
protected void processRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
final PrintWriter out = response.getWriter();
final Part filePart = request.getPart("file");
final String fileName = getFileName(filePart);
final String serverFilePath = getUploadFolder() + File.separator + fileName;
long timeBefore = System.currentTimeMillis();
String writeMethod = request.getParameter("writemethod");
if (writeMethod.equals("partwrite")) {
out.println("<br>Using Part.write method...");
filePart.write(serverFilePath);
} else {
out.println("<br>Using Part.write method...");
writeToFileUsingFileOutputStream(filePart.getInputStream(),
serverFilePath);
}
long timeAfter = System.currentTimeMillis();
out.println("<br>New file " + fileName + " created at " + serverFilePath);
out.println("<br>Time elapsed= " + (timeAfter - timeBefore));
}
private String getUploadFolder() { // synchronization
// not handled.
if (uploadFolder == null) {
String contextRealPath = getServletContext().getRealPath("/");
uploadFolder = contextRealPath + File.separator + uploadFolderName;
File dir = new File(uploadFolder);
if (!dir.exists()) {
boolean create = dir.mkdir();
if (create) {
System.out.println("Uploads directory created:" + uploadFolder);
} else {
throw new RuntimeException("Directory Cannot Be Created!");
}
}
}
return uploadFolder;
}
private void writeToFileUsingFileOutputStream(InputStream filecontent,
String filePath) throws IOException {
OutputStream out = null;
try {
out = new FileOutputStream(new File(filePath));
int read = 0;
final byte[] bytes = new byte[1024];
while ((read = filecontent.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
} finally {
if (out != null) {
out.close();
}
if (filecontent != null) {
filecontent.close();
}
}
}
private String getFileName(final Part part) {
final String partHeader = part.getHeader("content-disposition");
String[] sections = partHeader.split(";");
for (String content : sections) {
if (content.trim().startsWith("filename")) {
return content.substring(content.indexOf('=') + 1).trim()
.replace("\"", "");
}
}
return null;
}
}
You may play around with the attributes of @MultipartConfig. You may also use the mutipart-config element in the web.xml instead of the annotation.
More about getFileName method
A look at the request sent from client through a tool such as tcpmon or wireshark will give you a better idea. I have captured two screenshots from my tcpmon leaving some of the uploaded file contents from middle.
As you can see from the images, there is a part for all elements in the form. Each part starts with a boundary delimiter (e.g. ------WebKitFormBoundarysAGlmoydAOw5Bfh1). Every part also has a Content-Disposition header which contains information such as name of field, filename of the original file in case of files etc. So we have to get the part for file, get the Content-Disposition header for that part, split the header value using semicolon (;) into sections, iterate through the sections until you see a section starting with filename and then retrieve the filename.
You may want to package the application as a war, deploy it into tomcat and then start tomcat manually following steps mentioned @ http://javajee.com/packaging-and-deploying-a-web-application-into-standalone-apache-tomcat as you may find issues in finding the real path to save file when directly working from eclipse integrated with tomcat.
You should first run your html form (e.g. http://localhost:8080/Form-Processing/UploadForm.html where Form-Processing is my context folder in tomcat, UploadForm.html is the html form page which is created directly under the WebContent folder).
Select a file along with a write option, and upload it.
If upload is successful you will get the success page with information about the option you selected and time elapsed.
Eclipse Luna Java EE IDE for Web Developers and Apache Tomcat 8.0.18, using Servlet spec 3.1.
http://javajee.com/multipart-file-upload-in-servlet-30-using-multipart-config