SpringFileUpload

File upload with Spring

by John Kostaras


Java has long been accused of not providing an upload functionality out of the box. Jakarta Commons’ FileUpload and Jason Hunter’s COS are two well known file upload implementations. Spring provides an implementation for each one of the above via its MultipartResolver strategy interface.

In this Netbeans tutorial I 'll demonstrate two ways to use Spring's upload interface, one without and one with Command Beans.

1. Create a SpringMVC project

Let's start by creating a new Spring Web application (Risberg, 2008). The following screenshots will help you with the details.

image:Fig1.png
Figure 1 Create a new Web Application project

image:Fig2.png
Figure 2 Name project

image:Fig3.png
Figure 3 Choose application server

image:Fig4.png
Figure 4 Add Spring MVC support

Netbeans does a lot of work preparing a working skeleton Spring MVC project.

image:Fig5.png
Figure 5 Skeleton Spring MVC project generated by Netbeans

Let's go quickly through the generated code.

redirect.jsp

<%--
Views should be stored under the WEB-INF folder so that
they are not accessible except through controller process.

This JSP is here to provide a redirect to the dispatcher
servlet but should be the only JSP outside of WEB-INF.
--%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<% response.sendRedirect("index.htm"); %>


This page uses JSTL to redirect to index.htm. From
web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>*.htm</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>redirect.jsp</welcome-file>
    </welcome-file-list>
</web-app>

one can see that the extension *.htm is mapped to the DispatcherServlet. DispatcherServlet is Spring's front controller, the main servlet that accepts requests. DispatcherServlet uses dispatcher-servlet.xml to map requests to page controllers. The name of this xml file is produced, by convention, by adding -servlet to servlet-name in web.xml

<servlet-name>dispatcher</servlet-name>

E.g. if we had given the servlet-name this value

<servlet-name>springupload</servlet-name>

then, the xml file would have been: springupload-servlet.xml.
dispatcher-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/aop 
                           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
                           http://www.springframework.org/schema/tx 
                           http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    
    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
    
    <!--
    Most controllers will use the ControllerClassNameHandlerMapping above, but
    for the index controller we are using ParameterizableViewController, so we must
    define an explicit mapping for it.
    -->
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/index.htm">indexController</prop>
            </props>
        </property>
    </bean>
    
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />
    
    <!--
    The index controller.
    -->
    <bean name="indexController"
          class="org.springframework.web.servlet.mvc.ParameterizableViewController"
          p:viewName="index" />    
</beans>

The mapping from actions to controllers is done by using Spring's handler mappers. You can see two of them in dispatcher-servlet.xml: ControllerClassNameHandlerMapping and SimpleUrlHandlerMapping.
ControllerClassNameHandlerMapping maps a request to a controller by stripping Controller from the end of the class name and normalizing it to lowercase (Walls, 2008). E.g. a request /home.htm is automatically mapped to HomeController class.
However, as you will notice in the comment of dispatcher-servlet.xml

<!--
Most controllers will use the ControllerClassNameHandlerMapping above, but
for the index controller we are using ParameterizableViewController, so we must
define an explicit mapping for it.
-->

/index.htm request is handled differently, by the ParameterizableViewController. SimpleUrlHandlerMapping maps /index.htm to indexController

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/index.htm">indexController</prop>
        </props>
    </property>
</bean>

<bean name="indexController"
      class="org.springframework.web.servlet.mvc.ParameterizableViewController"
      p:viewName="index" />   

which is a ParameterizableViewController. As you can see from the above configuration, ParameterizableViewController returns a view with name index. The last thing the Dispatcher has to do is to map this name to an actual jsp page.
This is the job of the view resolver. InternalResourceViewResolver does exactly that:

<bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver"
      p:prefix="/WEB-INF/jsp/"
      p:suffix=".jsp" />

for each view name, like index, InternalResourceViewResolver prefixes it with /WEB-INF/jsp/ and suffixes in with .jsp resulting in the web page: /WEB-INF/jsp/index.jsp.

image:Fig5a.png
Figure 5a dispatcher-servlet.xml explained

index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!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>Welcome to Spring Web MVC project</title>
   </head>

   <body>
      <p>Hello! This is the default welcome page for a Spring Web MVC project.</p>
      <p><i>To display a different welcome page for this project, modify</i>
      <tt>index.jsp</tt> <i>, or create your own welcome page then change
      the redirection in</i> <tt>redirect.jsp</tt> <i>to point to the new
      welcome page and also update the welcome-file setting in</i>
      <tt>web.xml</tt>.</p>
   </body>
</html>

So, finally, if you run the application, you will end up with this web page:

Hello! This is the default welcome page for a Spring Web MVC project.

To display a different welcome page for this project, modify index.jsp , or create your own welcome
page then change the redirection in redirect.jsp to point to the new welcome page and also update the
welcome-file setting in web.xml.

A summary is shown in the following figure:
image:Fig6.png
Figure 6 Web flow

2. Create the upload application without command beans

Well, if you were able to understand how things worked in the previous section, I 'm happy; if not don't worry, we won't need all of them.

Modify index.jsp to allow for a file upload:
index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!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>File Upload</title>
    </head>
    
    <body>
	<h1>Please upload a file</h1>
	<form method="post" action="fileUpload.do" enctype="multipart/form-data">
		<input type="file" name="fileUploaded" /> <input type="submit" />
	</form>
    </body>
</html>

Two things to note. First, a file upload form must contain enctype="multipart/form-data". Second, optional, the action="fileUpload.do". I chose to create another pattern, .do, different from .htm, simply to distinguish between form actions and page requests. However, you can stick with .htm if you want (i.e. fileUpload.htm); it is simply a matter of taste.
To allow for .do actions, you need to modify web.xml adding the *.do pattern:
web.xml

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.htm</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.do</url-pattern>        
</servlet-mapping>

Next, we need to define the controller that will handle the request:
dispatcher-servlet.xml

<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

<bean name="/fileUpload.do" class="upload.FileUploadController"/>

/fileUpload.do is being mapped to FileUploadController. However, you also need to define a multipartResolver, and here I decided to use CommonsMultipartResolver, but CosMultipartResolver would also do the job.
And finally, the last step, implement the FileUploadController. Right-click on Source packages and select New -> Java Class and then fill in the class and package names from the source code below; then set the source code of the controller as:
FileUploadController

package upload;

import java.io.File;

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

import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

public class FileUploadController extends AbstractController {

    private static final String destinationDir = "C:/temp/";

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest req,
            HttpServletResponse res) throws Exception {
        res.setContentType("text/plain");
        if (!(req instanceof MultipartHttpServletRequest)) {
            res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expected multipart request");
            return null;
        }
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;
        MultipartFile file = multipartRequest.getFile("fileUploaded");
        File destination = new File(destinationDir + file.getOriginalFilename());
        file.transferTo(destination);
        res.getWriter().write("Success, wrote to " + destination.getAbsolutePath());        
        res.flushBuffer();
        return null;
    }
}

Spring MVC provides a number of Controllers to handle requests. Figure 7 shows the hierarchy of controllers:
image:Fig7.png
Figure 7 Spring MVC Controller hierarchy

I use the simplest controller here, AbstractController. In the next section, I 'll show you how to use SimpleFormController to perform the same task.

So, this controller, first rejects the request if it is not a multipart request. If it is, though, it gets the file as a MultipartFile and uploads it to the destination (C:/temp in my Windows machine, but you can set it at will). You may also choose to inject the directory via spring. Finally, it returns a message of success to the browser.

Please note that every Spring controller returns a ModelAndView object, which, as its name says, returns a model and a view to be rendered to the client. However, the FileUploadController does not return a ModelAndView object this time, but it overrides it (by returning null) in order to write a plain text page directly to HttpServletResponse. Of course, you could return a view (ModelAndView) that the view resolver would map to a .jsp page. This was to show that you do not have to always follow Spring's flow.

Are we done? Almost. Do not forget to add commons-io.jar and commons-fileupload.jar to your /WEB-INF/lib directory by defining it with Libraries in Netbeans.

If you run the application, you arrive at this screen:

image:Fig8.png
Figure 8 Spring MVC File Upload web application

Try to upload a file by clicking on Browse first to select a file, then on Submit. If you do, then you 'll receive an HTTP 404 error!!!! What went wrong? If you check at the logs, you will see the message

(/springupload/fileUpload.do) cannot be resolved

This means that spring cannot find how to handle fileUpload.do request. But we defined it in dispatcher-servlet.xml, right?

<bean name="/fileUpload.do" class="upload.FileUploadController"/>

Yep, but Netbeans, in order to make our life easier, defined ControllerClassNameHandlerMapping as our handler mapper. Recall from above, that this handler mapper maps a web page e.g. fileUpload.jsp to FileUploadController.java. So, either you have to rename index.jsp to fileUpload.jsp and of course do not forget to modify indexController's returned view in dispatcher-servlet.xml accordingly, or use BeanNameUrlHandlerMapping. BeanNameUrlHandlerMapping maps URLs to beans with names that start with a slash ("/").

I chose the second solution here, but feel free to experiment with the first, too. So add BeanNameUrlHandlerMapping by editing:
dispatcher-servlet.xml

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

Spring MVC does a great job by trying all handler mappers and trying to find a match. You may also define ordering by setting the "order" property.

Now we are done. Run the application again, upload a file and you will get the message of success:

Success, wrote to C:\temp\test.txt

Well done!

In the following figure we see the flow of events.
image:Fig9.png
Figure 9 Web flow

3. Create the upload application with command beans

As we saw in Figure 7, Spring provides a type of controller that is specific to form submissions, SimpleFormController.

Let's copy springupload project to e.g. springFileUpload by right clicking on springupload project and selecting Copy... from the pop-up menu.
First, let's modify FileUploadController.java

package upload;

import java.io.File;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BindException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;

public class FileUploadController extends SimpleFormController {
    private static final String destinationDir = "C:/temp/";
    
    @Override
    protected ModelAndView onSubmit(HttpServletRequest req, 
                                    HttpServletResponse res, 
                                    Object command, 
                                    BindException errors) throws Exception {
        res.setContentType("text/plain");
        if (!(req instanceof MultipartHttpServletRequest)) {
            res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expected multipart request");
            return null;
        }
        FileUploadBean bean = (FileUploadBean) command;
        MultipartFile file = bean.getFile();
        File destination = new File(destinationDir + file.getOriginalFilename());
        file.transferTo(destination);
        res.getWriter().write("Success, wrote to " + destination.getAbsolutePath());        
        res.flushBuffer();
        return null;
    }
}

Instead of extending AbstractController, FileUploadController extends SimpleFormController which is used specifically for form submissions. Instead of handleRequestInternal, here we have to override one of the following methods:

  • protected ModelAndView onSubmit(Object command) throws Exception;
  • protected ModelAndView onSubmit(Object command, BindException errors) throws Exception;
  • protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws Exception;
  • protected void doSubmitAction(Object command) throws Exception;

When overriding an onSubmit() method, you have to return a ModelAndView object. If you only need to perform an action on the command object and return the success view when this action completes, you can override the doSubmitAction() method instead, whose return type is void, and which will render the success view by default. In our case, because we need access to HttpServletRequest and HttpServletResponse objects, I chose to override the third method from above.

If you compare the onSubmit method with the handleRequestInternal of the previous section, you would see that the changes are only here:

protected ModelAndView handleRequestInternal(HttpServletRequest req, HttpServletResponse res) throws Exception {
    ...          
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;
        MultipartFile file = multipartRequest.getFile("fileUploaded");
protected ModelAndView onSubmit(HttpServletRequest req, 
                                HttpServletResponse res, 
                                Object command, 
                                BindException errors) throws Exception {
    ...
        FileUploadBean bean = (FileUploadBean) command;
        MultipartFile file = bean.getFile();

SimpleFormController uses a backing Java bean to handle the command, i.e. the object passed from the form:
FileUploadBean.java

package upload;

import org.springframework.web.multipart.MultipartFile;

public class FileUploadBean {
    private MultipartFile file;

    public MultipartFile getFile() {
        return file;
    }

    public void setFile(MultipartFile file) {
        this.file = file;
    }
}

Rename index.jsp to something more catchy like uploadform.jsp and modify it like so:
uploadform.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!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>File Upload</title>
    </head>
    
    <body>
	<h1>Please upload a file</h1>
	<form method="post" action="fileUpload.do" enctype="multipart/form-data">
		<input type="file" name="file" /> <input type="submit" />
	</form>
    </body>
</html>

Notice, that we modified name="file" of <input type="file" name="file" /> so as to match attribute private MultipartFile file; of FileUploadBean.java.

Fig.10
Figure 10 Map a form field to a Java bean property

You could have also used Spring's <form:form> tag:
uploadform.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!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>File Upload</title>
    </head>
    
    <body>
	<h1>Please upload a file</h1>
	<form:form commandName="fileUpload" action="fileUpload.do" enctype="multipart/form-data">
		<input type="file" name="file" /> <input type="submit" />
	</form:form>
    </body>
</html>

Finally, let's update
dispatcher-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/aop 
                           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
                           http://www.springframework.org/schema/tx 
                           http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    
    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
    
    <bean id="multipartResolver"
	class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
			
    <bean name="/fileUpload.do" class="upload.FileUploadController">
        <property name="commandName" value="fileUpload"/>
        <property name="commandClass" value="upload.FileUploadBean"/>
        <property name="formView" value="uploadform" />
    </bean>
            
    <!--
    Most controllers will use the ControllerClassNameHandlerMapping above, but
    for the index controller we are using ParameterizableViewController, so we must
    define an explicit mapping for it.
    -->
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/index.htm">indexController</prop>
            </props>
        </property>
    </bean>
    
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />
    
    <!--
    The index controller.
    -->
    <bean name="indexController"
          class="org.springframework.web.servlet.mvc.ParameterizableViewController"
          p:viewName="uploadform" />
</beans>

Notice, that the only change I needed to do because I changed the form name from index.jsp to uploadform.jsp was to change the returned view name from the indexController:

    <bean name="indexController"
          class="org.springframework.web.servlet.mvc.ParameterizableViewController"
          p:viewName="uploadform" />

However, the most important change is

    <bean name="/fileUpload.do" class="upload.FileUploadController">
        <property name="commandName" value="fileUpload"/>
        <property name="commandClass" value="upload.FileUploadBean"/>
        <property name="formView" value="uploadform" />
    </bean>

Notice, that we define the command class (the backing bean), a command name, and the form view.

Execute the project to get the same results as before.

3.1 Return a ModelAndView

Let's modify the controller to return a success view instead of writing directly to HttpServletResponse
FileUploadController.java

package upload;

import java.io.File;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BindException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;

public class FileUploadController extends SimpleFormController {
    private static final String destinationDir = "C:/temp/";
    
    @Override
    protected ModelAndView onSubmit(HttpServletRequest req, 
                                    HttpServletResponse res, 
                                    Object command, 
                                    BindException errors) throws Exception {
//        res.setContentType("text/plain");
        if (!(req instanceof MultipartHttpServletRequest)) {
            res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expected multipart request");
            return null;
        }
        FileUploadBean bean = (FileUploadBean) command;
        MultipartFile file = bean.getFile();
        File destination = new File(destinationDir + file.getOriginalFilename());
        file.transferTo(destination);
//        res.getWriter().write("Success, wrote to " + destination.getAbsolutePath());        
//        res.flushBuffer();
        return new ModelAndView(getSuccessView(),"destination",destination.getAbsolutePath());
    }
}

We return the success view and a parameter destination with value the absolute path of the uploaded file. Let's create the success view
success.jsp

<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>


<!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>Upload Success</title>
    </head>
    <body>
    <h1>Success, wrote to ${destination}.</h1>
    </body>
</html>

and define it in
dispatcher-servlet.xml

    <bean name="/fileUpload.do" class="upload.FileUploadController">
        <property name="commandName" value="fileUpload"/>
        <property name="commandClass" value="upload.FileUploadBean"/>
        <property name="formView" value="uploadform" />
        <property name="successView" value="success" />
    </bean>

Run the project again to get the success page.

3.2 Apply the Post/Redirect/Get Design Pattern

However, a problem still exists; if you press the Refresh button on the browser, the file is re-uploaded. You can test that by uploading a file, then delete it from C:\temp\, and then click on the Refresh button while in success page; the file will be uploaded again.

One way (Mak, 2008), the most difficult one but also the most flexible, is to do the following changes (a simpler way follows):
dispatcher-servlet.xml
a. add a new successController that will handle the request /success.htm,

    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/index.htm">indexController</prop>
                <prop key="/success.htm">successController</prop>
            </props>
        </property>
    </bean>

    <bean id="successController"
        class="org.springframework.web.servlet.mvc.ParameterizableViewController"
        p:viewName="success" /> 

b. add a new view resolver, ResourceBundleViewResolver setting its order to 0 to be executed first, and InternalResourceViewResolver next,

    <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
        p:basename="views" 
        p:order="0" />
    
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:order="1"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />

c. modify the successView property to now point to successRedirect

    <bean name="/formFileUpload.do" class="upload.FileUploadSimpleController">
        <property name="commandName" value="fileUpload"/>
        <property name="commandClass" value="upload.FileUploadBean"/>
        <property name="formView" value="uploadform" />
        <property name="successView" value="successRedirect" />
    </bean>

d. and create a new views.properties file in your classpath (e.g. inside your Java source package)

successRedirect.(class)=org.springframework.web.servlet.view.RedirectView
successRedirect.url=success.htm

This way, the success page does not resubmit the file.

Fig.11
Figure 11 How the Post/Redirect/Get Design Pattern works

A simpler way (e.g. Van de Velde T. et. al., 2008) is to configure your dispatcher-servlet.xml like so:

	
    <bean name="/fileUpload.do" class="upload.FileUploadSimpleController">
        <property name="commandName" value="fileUpload"/>
        <property name="commandClass" value="upload.FileUploadBean"/>
        <property name="formView" value="uploadform" />
        <property name="successView" value="redirect:success.htm" />
    </bean>
            
    <!--
    Most controllers will use the ControllerClassNameHandlerMapping above, but
    for the index controller we are using ParameterizableViewController, so we must
    define an explicit mapping for it.
    -->
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/index.htm">indexController</prop>
            </props>
        </property>
    </bean>
          
    <bean name="/success.htm"
        class="org.springframework.web.servlet.mvc.ParameterizableViewController"
        p:viewName="success" />   
</beans>

Notice the

<property name="successView" value="redirect:success.htm" />

that directly points to

    <bean name="/success.htm"
        class="org.springframework.web.servlet.mvc.ParameterizableViewController"
        p:viewName="success" />   

while there is no need for ResourceBundleViewResolver or views.properties.
However, if you redeploy and execute the project, you realise that we lost the destination variable:

Success, wrote to .

Well, model is not propagated during redirect! However, you will notice that the parameter destination is passed in the url:

success.htm?destination=C:%2Ftemp%2Ftest.txt

During redirect, the model object is not propagated to the request scope, but org.springframework.web.servlet.view.RedirectView converts all model attributes to Strings and adds them to the URL's query string (as Strings). If the model contains objects other than Strings, e.g. Collections etc. it is better to pass the model as a session variable.

So, how do we get the destination variable value from the query string? JSTL to the rescue (Geary D, 2002). In the following modified success.jsp we use the JSTL param implicit object to get the destination parameter:

<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>


<!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>Upload Success</title>
    </head>
    <body>
    <h1>Success, wrote to <c:out value='${param.destination}'/>.</h1>   
    </body>
</html>

Pay special attention to the param in front of destination. This is a special JSTL command to access the URL parameters.
Execute the web application again to get the correct:

Success, wrote to C:\temp\test.txt.

Summary

We went a long way to demonstrate some nice features of web development using SpringMVC. I showed and explained to you how to create a skeleton SpringMVC project using Netbeans 6.5. Then, we created a file upload web application without using command beans, and then by using command beans. We saw how to write directly to the HttpServletResponse and then how to return a ModelAndView. Finally, we saw how by applying the post/redirect/get design pattern and JSTL params we can avoid resubmitting a form when the user refreshes his/her brower.


References

  1. Risberg T. et al (2008), Developing a Spring Framework MVC application step-by-step using NetBeans and GlassFish.
  2. Ladd S. et al. (2006), Expert Spring MVC and Web Flow, Apress.
  3. Mak G. (2008), Spring Recipes: A Problem-Solution Approach, Apress.
  4. Van de Velde T. et. al. (2008), Beginning Spring Framework 2, Wrox.
  5. Walls G. (2008), Spring in Action, 2nd Edition, Manning.
  6. Geary D. (2002), Core JSTL: Mastering the JSP™ Standard Tag Library, Prentice Hall PTR, http://www.informit.com/articles/article.aspx?p=30946&seqNum=7
Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo