Spring has built-in multipart support to handle fileuploads in web
applications. The design for the multipart support is done with
pluggable MultipartResolver objects,
defined in the org.springframework.web.multipart
package. Out of the box, Spring provides a
MultipartResolver for use with
Commons FileUpload (http://jakarta.apache.org/commons/fileupload).
How uploading files is supported will be described in the rest of
this chapter.
By default, no multipart handling will be done by Spring, as some
developers will want to handle multiparts themselves. You will have to
enable it yourself by adding a multipart resolver to the web
application's context. After you have done that, each request will be
inspected to see if it contains a multipart. If no multipart is found,
the request will continue as expected. However, if a multipart is found
in the request, the MultipartResolver that has
been declared in your context will be used. After that, the multipart
attribute in your request will be treated like any other
attribute.
The following example shows how to use the
CommonsMultipartResolver:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- one of the properties available; the maximum file size in bytes --> <property name="maxUploadSize" value="100000"/> </bean>
This is an example using the
CosMultipartResolver:
<bean id="multipartResolver" class="org.springframework.web.multipart.cos.CosMultipartResolver"> <!-- one of the properties available; the maximum file size in bytes --> <property name="maxUploadSize" value="100000"/> </bean>
Of course you also need to put the appropriate jars in your
classpath for the multipart resolver to work. In the case of the
CommonsMultipartResolver, you need to use
commons-fileupload.jar; in the case of the
CosMultipartResolver, use
cos.jar.
Now that you have seen how to set Spring up to handle multipart
requests, let's talk about how to actually use it. When the Spring
DispatcherServlet detects a multi-part request,
it activates the resolver that has been declared in your context and
hands over the request. What the resolver then does is wrap the current
HttpServletRequest into a
MultipartHttpServletRequest that has support for
multipart file uploads. Using the
MultipartHttpServletRequest you can get
information about the multiparts contained by this request and actually
get access to the multipart files themselves in your controllers.
After the MultipartResolver has finished
doing its job, the request will be processed like any other. To use it,
you create a form with an upload field (see immediately below), then let
Spring bind the file onto your form (backing object). To actually let
the user upload a file, we have to create a (HTML) form:
<html> <head> <title>Upload a file please</title> </head> <body> <h1>Please upload a file</h1> <form method="post" action="upload.form" enctype="multipart/form-data"> <input type="file" name="file"/> <input type="submit"/> </form> </body> </html>
As you can see, we've created a field named after the property of
the bean that holds the byte[]. Furthermore we've
added the encoding attribute
(enctype="multipart/form-data") which is necessary to
let the browser know how to encode the multipart fields (do not forget
this!).
Just as with any other property that's not automagically
convertible to a string or primitive type, to be able to put binary data
in your objects you have to register a custom editor with the
ServletRequestDatabinder. There are a couple of
editors available for handling files and setting the results on an
object. There's a StringMultipartEditor capable
of converting files to Strings (using a user-defined character set) and
there is a ByteArrayMultipartEditor which
converts files to byte arrays. They function just as the
CustomDateEditor does.
So, to be able to upload files using a (HTML) form, declare the resolver, a url mapping to a controller that will process the bean, and the controller itself.
<beans> <!-- lets use the Commons-based implementation of the MultipartResolver interface --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <value> /upload.form=fileUploadController </value> </property> </bean> <bean id="fileUploadController" class="examples.FileUploadController"> <property name="commandClass" value="examples.FileUploadBean"/> <property name="formView" value="fileuploadform"/> <property name="successView" value="confirmation"/> </bean> </beans>
After that, create the controller and the actual class to hold the file property.
public class FileUploadController extends SimpleFormController { protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws ServletException, IOException { // cast the bean FileUploadBean bean = (FileUploadBean) command; let's see if there's content there byte[] file = bean.getFile(); if (file == null) { // hmm, that's strange, the user did not upload anything } // well, let's do nothing with the bean for now and return return super.onSubmit(request, response, command, errors); } protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws ServletException { // to actually be able to convert Multipart instance to byte[] // we have to register a custom editor binder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor()); // now Spring knows how to handle multipart object and convert them } } public class FileUploadBean { private byte[] file; public void setFile(byte[] file) { this.file = file; } public byte[] getFile() { return file; } }
As you can see, the FileUploadBean has a
property typed byte[] that holds the file. The
controller registers a custom editor to let Spring know how to actually
convert the multipart objects the resolver has found to properties
specified by the bean. In this example, nothing is done with the
byte[] property of the bean itself, but in practice
you can do whatever you want (save it in a database, mail it to
somebody, etc).
An equivalent example in which a file is bound straight to a String-typed property on a (form backing) object might look like:
public class FileUploadController extends SimpleFormController { protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws ServletException, IOException { // cast the bean FileUploadBean bean = (FileUploadBean) command; let's see if there's content there String file = bean.getFile(); if (file == null) { // hmm, that's strange, the user did not upload anything } // well, let's do nothing with the bean for now and return return super.onSubmit(request, response, command, errors); } protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws ServletException { // to actually be able to convert Multipart instance to a String // we have to register a custom editor binder.registerCustomEditor(String.class, new StringMultipartFileEditor()); // now Spring knows how to handle multipart object and convert them } } public class FileUploadBean { private String file; public void setFile(String file) { this.file = file; } public String getFile() { return file; } }
Of course, this last example only makes (logical) sense in the context of uploading a plain text file (it wouldn't work so well in the case of uploading an image file).
The third (and final) option is where one binds directly to a
MultipartFile property declared on the
(form backing) object's class. In this case one does not need to
register any custom PropertyEditor
because there is no type conversion to be performed.
public class FileUploadController extends SimpleFormController { protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws ServletException, IOException { // cast the bean FileUploadBean bean = (FileUploadBean) command; let's see if there's content there MultipartFile file = bean.getFile(); if (file == null) { // hmm, that's strange, the user did not upload anything } // well, let's do nothing with the bean for now and return return super.onSubmit(request, response, command, errors); } } public class FileUploadBean { private MultipartFile file; public void setFile(MultipartFile file) { this.file = file; } public MultipartFile getFile() { return file; } }