Wednesday, October 8, 2008

Spring MVC, Multiple/Arbitrary Number of File Uploads

Handle an arbitrary number of file uploads in a single form submission. This assumes you are using some javascript on your page to create new file upload form elements as your user requests them (like the add attachment button in gmail).

Using Spring's normal command bean binding or annotated method way of doing things requires you to know exactly the number of file uploads you expect at compile time.

Using Spring MVC 2.5:

@RequestMapping("/uploadFiles")
public String uploadFiles(HttpServletRequest request) {
if (request instanceof AbstractMultipartHttpServletRequest == false) {
throw new RuntimeException("Did not recieve an instance of AbstractMultipartHttpServletRequest.");
}
Map<String, MultipartFile> paramToFileMap = ((AbstractMultipartHttpServletRequest) request).getFileMap();
for (String fileParameter : paramToFileMap.keySet()) {
MultipartFile file = paramToFileMap.get(fileParameter);
// do something here...

}
return "uploadSuccess";
}

Spring MVC 2.5, Annotations, Controller, MultipartFile

Hello search-engine user,

The exception you experienced...


org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.web.multipart.MultipartFile]: Specified class is an interface
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:75)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveModelAttribute(HandlerMethodInvoker.java:357)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:201)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:128)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:310)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:292)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:875)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:809)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
at java.lang.Thread.run(Thread.java:613)


... is caused by not annotating your method parameter with @RequestParam.


@RequestMapping("/uploadFile", method=RequestMethod.POST)
public String uploadFile(@RequestParam(value="myFile", required=true) MultipartFile file) {
...
}

Since spring does not know what upload parameter goes with MultipartFile without the annotation, it attempts to create a new default instance of the parameter to pass in. It can't do that for an interface, hence the error message.