Sep 1, 2012

Form-based File Upload in J2EE Web Application Part 1

Many times we may have a file upload control on view asking user to upload a file or an image from local machine. HTML provides  a file control to upload a file. But on server side, we need to write a code to save it on a file server.

In J2EE, file upload functionality can be best achieved using Commons File Upload. A file upload request comprises an ordered list of items that are encoded according to RFC-1867. FileUpload can parse such a request and provide your application with a list of the individual uploaded items. Each such item implements the FileItem interface, regardless of its underlying implementation.

Each file item has a number of properties that might be of interest for your application. For example, every item has a name and a content type, and can provide an InputStream to access its data. On the other hand, you may need to process items differently, depending upon whether the item is a regular form field - that is, the data came from an ordinary text box or similar HTML field - or an uploaded file. The FileItem interface provides the methods to make such a determination, and to access the data in the most appropriate manner.

Before you can work with the uploaded items, of course, you need to parse the request itself. Ensuring that the request is actually a file upload request is straightforward, but FileUpload makes it simplicity itself, by providing a static method to do just that.

Let's make a very simple application to upload a file using JSP/Servlets as shown in following image.

Prerequisites


For this tutorial, we will need the following tools: (The older or newer version should also works). Moreover, basic Java knowledge is assumed.

  1. Eclipse IDE for Java EE Developers
  2. Apache Tomcat v6 or later
  3. Apache Commons IO 
  4. Apache Commons File Upload
Apache commons IO and File Upload jars has to be put in your web application lib folder. Following is the folder structure for this example.


Create a View using JSP


The view includes a simple file upload control with submit button. Following is the code:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!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=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>

<form action="upload.do" method="post" enctype="multipart/form-data">
 Select file to upload: <input type="file" name="selectFile" /> <br />
 <input type="submit"> 
</form>


</body>
</html> 

 

Creating a Controller 

Controller will receive the multipart request. It will save the file at folder location configured in context parameters of the web application. It also creates a map of regular form parameters if any. We dont have any regular form parameter in this example. But that's put for your reference only.

package org.avid.upload.controller;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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;

/**
 * @author Jay Rajani
 * 
 * Servlet implementation class UploadController
 */
public class UploadController extends HttpServlet {
 private static final long serialVersionUID = 1L;
 
 private String folderLocation = null; 
 
 @Override
 public void init() throws ServletException {
  super.init();
  this.folderLocation = getServletContext().getInitParameter("UPLOAD_FOLDER");
 }

 /**
  * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
  */
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  
  HashMap<String, String> formParams = new HashMap<String, String>();
  
  // Check that we have a file upload request
  boolean isMultipart = ServletFileUpload.isMultipartContent(request);
  
  try{
   if (isMultipart){
    
    // Create a factory for disk-based file items
    FileItemFactory factory = new DiskFileItemFactory();

    // Create a new file upload handler
    ServletFileUpload upload = new ServletFileUpload(factory);

    // Parse the request
    List<FileItem> items = upload.parseRequest(request);
    
    // Process the uploaded items
    Iterator<FileItem> iter = items.iterator();
    while (iter.hasNext()) {
        FileItem item = (FileItem) iter.next();

        if (item.isFormField()) {
         
         // Process a regular form field
         formParams.put(item.getFieldName(), item.getString());
        } else {
         
         // Process a file upload
         String fileName = item.getName();
         
         File uploadedFile = new File(folderLocation+File.separator+fileName);
            item.write(uploadedFile);
        }
    }
   }
  }catch(FileUploadException fue){
   fue.printStackTrace();
   throw new ServletException(fue.getMessage());
  }catch(Exception e){
   e.printStackTrace();
   throw new ServletException(e.getMessage());
  }
 }

}


Deployment Descriptor


Here is the snippet of final deployment descriptor.

<?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">
 
 <context-param>
  <param-name>UPLOAD_FOLDER</param-name>
  <param-value>E:/temp</param-value>
 </context-param>
 
 <display-name>FileUpload</display-name>
 
 <servlet>
  <description>
  </description>
  <display-name>UploadController</display-name>
  <servlet-name>UploadController</servlet-name>
  <servlet-class>
  org.avid.upload.controller.UploadController</servlet-class>
 </servlet>
 <servlet-mapping>
  <servlet-name>UploadController</servlet-name>
  <url-pattern>/upload.do</url-pattern>
 </servlet-mapping>

</web-app>

The code will save the file to location specified in web.xml. It can be a local folder or shared folder or ftp location depending on your requirement.

4 comments:

Anonymous said...

Really Nice Blog
but something is missing
Can u add some more field in ur form like
input type="text" name="Uname"

and then u try to access that Uname in servlet like(String name=request.getParameter("Uname"));
u'll get null.

give me reason.
Thanks in advanced.

Anonymous said...

i try with this commons.fileupload jar but it's not working well.
so i write code like this.nd it's working fine. U can try this.

String saveFile="";
String contentType = request.getContentType();
if((contentType != null)&(contentType.indexOf("multipart/form-data") >= 0)){
DataInputStream in = new DataInputStream(request.getInputStream());
int formDataLength = request.getContentLength();
byte dataBytes[] = new byte[formDataLength];
int byteRead = 0;
int totalBytesRead = 0;
while(totalBytesRead < formDataLength){
byteRead = in.read(dataBytes, totalBytesRead,formDataLength);
totalBytesRead += byteRead;
}
String file = new String(dataBytes);
saveFile = file.substring(file.indexOf("filename=\"") + 10);
saveFile = saveFile.substring(0, saveFile.indexOf("\n"));
saveFile = saveFile.substring(saveFile.lastIndexOf("\\") + 1,saveFile.indexOf("\""));
int lastIndex = contentType.lastIndexOf("=");
String boundary = contentType.substring(lastIndex + 1,contentType.length());
int pos;
pos = file.indexOf("filename=\"");
pos = file.indexOf("\n", pos) + 1;
pos = file.indexOf("\n", pos) + 1;
pos = file.indexOf("\n", pos) + 1;
int boundaryLocation = file.indexOf(boundary, pos) - 4;
int startPos = ((file.substring(0, pos)).getBytes()).length;
int endPos = ((file.substring(0, boundaryLocation)).getBytes()).length;
File ff = new File("C:/Users/Acme-8/Desktop/Bhavin-J2EE WorkSpace(2)/Lorenzo/WebContent/images/"+saveFile);
FileOutputStream fileOut = new FileOutputStream(ff);
fileOut.write(dataBytes, startPos, (endPos - startPos));
System.out.println("New File Created");
System.out.println(ff.getAbsolutePath());
fileOut.flush();
fileOut.close();
Connection con = null;
try {
con = DemoConnectionPool.getConnection();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
String query = "insert into ProductDetail(Product_Category,Tile_Name,Size,Series,Description,Image) values(?,?,?,?,?,?)";

PreparedStatement pstmt = null;
try {
pstmt = con.prepareStatement(query);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // create a statement
try {
pstmt.setString(1, "s1"); // set input parameter 1
pstmt.setString(2, "s1"); // set input parameter 2
pstmt.setString(3, "s1"); // set input parameter 3
pstmt.setString(4, "s1");
pstmt.setString(5, "s1");
pstmt.setString(6,ff.getPath());
int row=pstmt.executeUpdate(); // execute insert statement
if (row > 0) {
System.out.println( "File uploaded and saved into database");
PrintWriter out=response.getWriter();
RequestDispatcher rd2=request.getRequestDispatcher("addproduct.jsp");
out.print("
color='red'>Succesfully Product Added");
rd2.include(request, response);
}
} catch (Exception e) {
// TODO: handle exception
}
}

Jay Rajani said...

The reason you wont get "UName" with request.getParameter is that the form is multipart. So each part may contain different like file item or form field.

Please check UploadController line no 63

if (item.isFormField()) {

// Process a regular form field
formParams.put(item.getFieldName(), item.getString());
}

This is how you can read parameter like "UName".

In the code, I have created a formParam map of all form fields. So you can just call formParams.get("UName")

Hope my answer satisfies your query

Jay Rajani said...

The code that you have provided as an alternative is not wrong I would say.

Moreover, commons fileupload is a very well known API to use. So you should be able to achieve it using commons file upload.

The code that you have provided requires so much of reconsideration keeping Java practices in mind.