skip to main | skip to sidebar

Sunday, August 15, 2010

File Uploading Using Servlets, JSP and Commons File Upload API

I’ve seen many developers who are at the early stages of their career have problems with this topic and seen many posts in forums asking how to do it – File Uploading using Servlets, JSP!; this article will provide an example using Commons File Upload API. I tried to make the example as simple as possible, hope it helps those early birds. Example uses JSP to provide the pages with form where user can select the file to upload with other form fields, Commons File Upload API to process submitted form and read form fields separately, and Servlets as middle layer between JSP and Commons File Upload API, example also has ANT build script to generate the distributables. All the code can be downloaded, links to these resources are provided at the end of this post, lets get on with the example then.
The flow in this example is as depicted in the following picture.
Files list
As you can see, user selects the file to upload and provides normal form data as well, using "index.jsp" and submits the form to "FileUploadServlet" servlet. This servlet passes the request object to one of FormUtils class methods, and this method parses the request and creates "TestForm" class object out of the request object. This "TestForm" class object will be passed to "form-data.jsp" which in turn displays the provided data.
First we’ll go through the JSP files, we’ve two JSP files - "index.jsp", and "form-data.jsp" in this example, one to show the form with fields which user will submit, and another to show the submitted form data. Lets see what "index.jsp" has.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
    <title>Commons File Upload Example</title>
    <style type="text/css">
        body { font: 13px arial,helvetica,clean,sans-serif; text-align: center; }
        .container { text-align: left; width: 25em; border: 1px solid #000; padding: 0.5em; }
        .form-field { margin-bottom: 0.5em; }
        .form-field input { border: 1px solid #aaa; padding: 0.2em; }
        .btn-container { text-align: right; }
    </style>
</head>
<body>
    <div class="container">
    <form action="uploadServlet" method="post" enctype="multipart/form-data">
        <div class="form-field">
            <label>Your Name: </label>
            <input type="text" name="name" size="20" />
        </div>
        <div class="form-field">
            <label>Your File: </label>
            <input type="file" name="document" size="25" />
        </div>
        <div class="btn-container">
            <input type="submit" value="Submit"/>
        </div>
    </form>
    </div>
</body>
</html>
Above code has nothing special in it, just plain HTML, you might want to note two parts which are important here, one the "form" tag which has an extra attribute "enctype" and the other "input" tag with 'type' attribute as 'file'. When you want to submit a file to the server you need to have the 'enctype' attribute set to 'multipart/form-data'. And to have a form field where user can select a file and upload, you need to use the input tag with its type as 'file'. If you observe this page has one 'form' tag with 'action' attribute set to 'uploadServlet', which indicates that when user submits this form by clicking on the submit button, it'll submit the form data to a Servlet with its mapping URL ‘uploadServlet’. This form tag has two form fields – one text field with 'name' attribute as 'name' and the other a 'file' input tag with name attribute as 'document'. Now lets see the second JSP - 'form-data.jsp'.
<%@page import="com.rakesh.fileupload.form.TestForm"%>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
    <title>Commons File Upload Example</title>
</head>
<body>
    <%
    String error = (String)request.getAttribute("ERROR");
    if (error != null) {
    %>
        <h3><%= error %></h3>
    <%
    } else {
        TestForm form = (TestForm)request.getAttribute("DATA");
    %>
    <h2>Data Submitted is:</h2>
    <p>
        Your Name: <%=form.getName()%><br/>
        File Name: <%= form.getDocument().getName() %><br/>
        File Size: <%= form.getDocument().getSize() %><br/>
        File Type: <%= form.getDocument().getContentType() %><br/>
    <% } %>
    </p>
</body>
</html>
Even this JSP looks very simple except, its not simple HTML code. This page is supposed to show the data submitted by the user through 'index.jsp'. After the 'body' tag we'll first check for the request attribute 'ERROR' – when user submits the form, we'll check the submitted data and if we find any problem with the submitted data, we'll set this 'ERROR' request attribute and forward to this page. If this request attribute is set that means we have some problem with the submitted data, this error message has to be displayed to the user. We'll compare this request attribute with ‘null’ if its not null, we’ll display the error message. If this request attribute is ‘null’ – which means there is no problem with the submitted data, and we need to display the submitted information to the user, the ‘else’ block in the above code will do this for us. First line in the else block will look for a request attribute “DATA”, when the form is submitted we’ll parse the submitted request data and create one object out the those values – which is represented in our example with ‘TestForm’ class, its a POJO [Plain Old Java Object], with the corresponding fields we have in ‘index.jsp’. As mentioned earlier, ‘index.jsp’ has two form fields - ‘name’ and ‘document’, ‘TestForm’ class will have two fields in it one to hold the ‘name’ text field value and the other to hold the ‘document’ field value. Lets see the ‘TestForm’ class.
package com.rakesh.fileupload.form; 
import org.apache.commons.fileupload.FileItem;
public class TestForm {     private String name;     private FileItem document;
    public String getName() {         return name;     }
    public void setName(String name) {         this.name = name;     }
    public FileItem getDocument() {         return document;     }
    public void setDocument(FileItem document) {         this.document = document;     } }
Nothing special even here, plain Java class with two fields and setter/getter method for each of them. As the ‘name’ form field in the ‘index.jsp’ represents plain string value, the ‘name’ field in ‘TestForm’ is declared as ‘String’, and ‘document’ form field represents a file, we used “FileItem” class of Commons File Upload API. This class will provide all the information about the file which is uploaded and the provides access to the input stream for the file data. Now lets see the code which actually parses the submitted request and creates object of TestForm class.
package com.rakesh.fileupload.form; 
import java.lang.reflect.InvocationTargetException; import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class FormUtils {
    private static void processFormField(TestForm form, FileItem item)             throws IllegalArgumentException, IllegalAccessException {         try {             BeanUtils.setProperty(form, item.getFieldName(), item.getString());         } catch (InvocationTargetException e) {             // error while populating bean             return;         }     }
    private static TestForm processFileItems(List<FileItem> items)             throws IllegalArgumentException, IllegalAccessException {         TestForm form = new TestForm();         for (FileItem item : items) {             if (item.isFormField()) {                 processFormField(form, item);                 continue;             }             form.setDocument(item);         }         return form;     }
    @SuppressWarnings("unchecked")     public static TestForm getTestForm(HttpServletRequest request,             ServletFileUpload handler) throws FileUploadException,             IllegalArgumentException, IllegalAccessException {         return processFileItems(handler.parseRequest(request));     } }
This is a utility class which we use to parse the request object and create ‘TestForm’ class object out of the submitted values. It has two private static methods internally calling others and one public static method, which is the entry point for parsing. We use Commons Bean Utils API to populate the fields of ‘TestForm’ with the values submitted by the user. Lets go through these methods one by one. First the ‘processFormField()’ – this method will be called for each form field found in the request object. It takes two parameters.
  • TestForm form – The form object which we are trying to populate with the submitted values.
  • FileItem item – Form field value we are processing.
This method uses Commons Bean Utils API to populate the given form field in the given form object.  We’ll call the ‘BeanUtils.setProperty()’ method to populate the given field. This method takes three arguments.
  • Object bean – The form object which needs to be populated, in our case its TestForm class object.
  • String name – The Field name to be populated.
  • Object value – The value to set.
Second method in this class is ‘processFileItems()’ – this method will be called once by passing all form values as a ‘java.util.List’. It takes one parameter ‘List<FileItem'>’ which contains all the form fields submitted. We’ll loop through this list and check whether its a simple form field or a file. This ‘org.apache.commons.fileupload.FileItem’ class provides one method ‘isFormField()’ which returns true if the represented field is simple form field, false otherwise. If its a plain form field we’ll call the ‘processFormField()’ and pass necessary parameters to populate the ‘TestForm’ class object. If its not a plain form field, we’ll set it for ‘document’ field in ‘TestForm’ class object by calling ‘setDocument()’ method.
The only public method available in this class is ‘getTestForm()’ – this method will be called from the Servlet to which the form gets submitted. This method takes two parameters – one ‘HttpServletRequest’ object and the other is ‘ServletFileUpload’ class object which is from Commons File Upload API. This object allows us to process the request object and give all the form field values as instances of ‘FileItem’. We dont have to write any code to process the request object, just by calling the ‘parseRequest()’ on this object will give a ‘List’ of ‘FileItem’ object each representing one form field submitted. ‘getTestForm()’ method does nothing more than calling ‘parseRequest()’ method and passing the returned list to ‘processFileItems()’ method. This method in turn returns ‘TestForm’ class object after populating it with the form field values.
Now lets see the Servlet code
package com.rakesh.fileupload.web; 
import java.io.IOException;
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload;
import com.rakesh.fileupload.form.FormUtils; import com.rakesh.fileupload.form.TestForm;
public class FileUploadServlet extends HttpServlet {     private static final long serialVersionUID = 5334402393174853293L;
    private static final String UPLOAD_ERROR = "Error while processing values in request object!";     private static final String FAILURE_MSG = "Error while uploading file!";
    private ServletFileUpload handler;
    @Override     public void init() throws ServletException {         super.init();         DiskFileItemFactory factory = new DiskFileItemFactory();         handler = new ServletFileUpload(factory);     }
    @Override     public void destroy() {         super.destroy();         handler = null;     }
    @Override     protected void doPost(HttpServletRequest request,             HttpServletResponse response) throws ServletException, IOException {         try {             TestForm form = FormUtils.getTestForm(request, handler);             request.setAttribute("DATA", form);         } catch (FileUploadException e) {             e.printStackTrace();             request.setAttribute("ERROR", UPLOAD_ERROR);         } catch (Exception e) {             e.printStackTrace();             request.setAttribute("ERROR", FAILURE_MSG);         }         request.getRequestDispatcher("/WEB-INF/jsp/form-data.jsp").forward(                 request, response);     } }
This ‘FileUploadServlet’ overrides three methods - ‘init()’, ‘destroy()’, and ‘doPost()’. Well you might know that ‘init()’ will be called when the servlet gets initialized, and ‘destroy()’ will be called when the servlet container unloads the servlet. ‘doPost()’ will be called on form submit. As ‘init()’ and ‘destroy()’ are called once in servlet’s lifecycle, they are the best places to initialize resources which are used through out the servlet’s lifecycle. In our case the ‘ServletFileUpload’ object which we’ll be using to parse the request object and get the ‘TestForm’ with submitted form values is being initialized in ‘init()’ method and destroyed in ‘destroy()’ method. Now lets look at the ‘doPost()’ method. This method calls ‘FormUtils.getTestForm()’ method from inside ‘try…catch’ block, this method returns ‘TestForm’ object after populating it with the submitted values. Then we’ll set it as request attribute in the next line, and forward to the ‘form-data.jsp’ page. We’ve two catch blocks one catching ‘FileUploadException’ which will be thrown by the Commons File Upload API’s ‘parseRequest()’ method when it encounters problem parsing the request object. In both the catch blocks we’ll set one error message as request attribute ‘ERROR’ and forward to ‘form-data.jsp’. Well thats all the code we’ve to implement file uploading using Common File Upload API.
If you want to have more form fields, then add them first to the ‘index.jsp’ and then to the ‘TestForm’ class, add corresponding getter/setter methods to those new fields, this is enough have them populated, no other code changes are needed. To display these new fields you might need to change the ‘form-data.jsp’ file. All the files which are part of this example are listed in the below given diagram.
Files list
We need to add three jar files to the application ‘lib’ directory - ‘commons-beanutils-core.jar’, ‘commons-fileupload-1.2.1.jar’ and ‘commons-io-1.4.jar’ which are being used in the example code. I placed ‘form-data.jsp’ in “WEB-INF/jsp” directory, because this page should not accessible directly by typing the URL into the browser address bar. All the files inside ‘WEB-INF’ are not accessible directly for the user, but you can still use them from the servlet – as we did in our servlet.
Please find the links to the source code below in resources section. The ‘file-upload.rar’ file has eclipse project files in it, you can create eclipse project directly from the extracted directory. After creating project from the directory you need to add the ‘servlet-api.jar’ and ‘jsp-api.jar’ to project classpath. The ANT script provided is ready to use, except that you need to update the tomcat server location in it, this is needed to have the ‘servlet-api.jar’ and ‘jsp-api.jar’ in project classpath to compile and build the web application.

Resources:

Download source code.

Example WAR file.

Apache Tomcat.

Commons File Upload API

17 comments:

Tiger said...

Rakesh, I need a file uploader that can upload 5 GB files... can u build one for me?

Rakesh Reddy said...

I think the solution I provided to you, will suite your requirements :)

kishore said...

hi Rakesh how r u i cont able to convert a string to datetime to insert into sql server

my string is like this :"100928000000"
format is "yymmddhhmmss"
i want to insert in to database in datatime format
please do needfull

Rakesh Reddy said...

Well, this is not related to this post, you can use the "Write Me" link if you dont find any related posts for you question.
Anyway, you can use java.text.SimpleDateFormat for this purpose.
DateFormat formatter = new SimpleDateFormat("yyMMddHHmmss");
Then call the parse() method on this object to convert the string object to Date object.

Jorge Cid said...

mmm.... And the files where are saved? did you implemented write file on the server?

Rakesh Reddy said...

@Jorge - I didn't implemented that thinking that'll result bit more complexity to the example. You can just do it in simple way using streams. You can pass the directory location as configuration value to the servlet and save the files in that directory.

Jorge Cid said...

Hi Rakesh, thanks for you easy answer, I was added a llitle bit lines of code on FormUtils.java file and now I can upload (save/write) files on the server, maybe it's not a best practice however works fine.

Do you have another idea for this solution??

Regards,

Jorge

The code:

public class FormUtils {
private static void processFormField(TestForm form, FileItem item)
throws IllegalArgumentException, IllegalAccessException {
try {

BeanUtils.setProperty(form, item.getFieldName(), item.getString());
} catch (InvocationTargetException e) {
// error while populating bean
return;
}
}

private static TestForm processFileItems(List items)
throws IllegalArgumentException, IllegalAccessException {

//Begin my code:

File location = new File("D:\\Documents and Settings\\jorge.cid\\IBM\\rationalsdp7.0\\workspace\\uploadFiles");

TestForm form = new TestForm();
for (FileItem item : items) {
if (item.isFormField()) {
processFormField(form, item);
continue;
}
form.setDocument(item);

String fileName = item.getName();
String slashType = ( fileName.lastIndexOf( "\\" ) > 0 ) ? "\\" : "/"; // Windows or Linux
int startIndex = fileName.lastIndexOf( slashType );
String myFileName = fileName.substring( startIndex + 1, fileName.length() );

File fileUpload = new File (location, myFileName);
try {
item.write(fileUpload);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//End My code
return form;
} @SuppressWarnings("unchecked")
public static TestForm getTestForm(HttpServletRequest request,
ServletFileUpload handler)
throws FileUploadException,
IllegalArgumentException, IllegalAccessException {
return processFileItems(handler.parseRequest(request));
}
}

Rakesh Reddy said...

I've few things to mention here.
1. FormUtils written to give you one Object with all the values processed from HTTP request. So, it should not do any file IO.
2. If you want to save files to disk, first get the form object using FormUtils.getTestForm(), then pass TestForm object to a model class/DAO class which does the work of writing file and other parameters to disk/database.

About the location to store the files, you can opt for following options.
1. You can try by passing it using context parameters.
2. You can have a .properties file which will have the files location as property. Place this in app server directory which is visible to your web application and read the directory location from this property file.
If you are using Tomcat you can place the properties file in TOMCAT_HOME/shared/classes directory. By doing this way you can change this value any time and you dont have to rebuild your web application for this change. And this makes your application portable between Windows and Unix. You only need to change the path in properties file.

Hope this helps!

WASEEM said...

HI Rakesh ! I had run this application , this application only shows the amount of data submitted, but where that actual file is being uploaded? plz can u tel how to check that uploaded file.
My task is to upload a file at any given http address. plz do the needful. . .

Rakesh Reddy said...

Hi, I didn't wrote the code to save the uploaded file, as that is directly depends on the environment. If you see my earlier comments, i've pointed few options to implement this.

If you still cann't figure out how to do this, write to me with some details what exactly you want to do, so that i can give you a solution.

You can use the 'Write Me' link to write to me.

CalicoBeard said...

Rakesh,
Thank you so much for this tutorial! I found it invaluable for the project I am currently working on. I had to take it one step further and attach the uploaded file to an email. But this post cleared the way for my understanding of the FileUpload library.
Thanks again for your help!

Rakesh Reddy said...

I am Glad it helped you :)

Sapan Diwakar said...

Hi Rakesh,

I am not sure whether this is a right place to ask this. But i am trying to do slightly opposite to one above described. I am trying to pass a file name to JSP from Servlet using
request.setAttribute("filename", filepath);

But my jsp instead of using file it is printing file path. Do you know the way to help me out here?

Any help will be deeply appreciated.

Rakesh Reddy said...

Hi Diwakar, You've to read the file by yourself and print it on the JSP. JSP file itself can not handle it.

You can send me your case using the 'Write Me' link, so that I can make an example and send it to you[OR post that example here, so that others can see it :)].

@Santosh said...

Hi Rakesh,

I want to have two submit button, one for validating the file and one for uploading a file and a single browse. How can i achive this?
Santosh

Sumit Raj said...

Hi Rakesh. Thanks for the post. I am looking to upload multiple files at a time in a jsp like we do in facebook(using ctrl button we select 5 or 6 files). Can you help?

Rakesh A said...

Hi, file input tag has new attribute 'multiple' which is part of HTML5 and supported by most of the browsers, but for older browsers like 'IE9' and older, you've to opt for different solution, try to go through 'http://stackoverflow.com/questions/1593225/how-to-select-multiple-files-with-input-type-file'. On server I think, its more or less same, although I didn't try.

Post a Comment