Spring build-in multipart fileupload support
Spring 内置了对 web 应用 multipart(文件上传) 的支持,即通过可拔插的 MultipartResolver 对象实现对 multipart 的处理。该对象定义在 org.springframework.web.multipart
包内,它是按照 RFC 1867 设计的一个策略接口,自 Spring 2.5 开始,Sping 内包含了一个它的具体实现类,即 org.springframework.web.multipart.commons.CommonsMultipartResolver,该实现类是建立在 Jakarta Commons FileUpload 基础上的,因此在使用时需要引入对 ‘commons-fileupload.jar’ 的依赖。
默认情况下,Spring 是不对 multipart 进行处理的,你可以在你的 web 应用上下文配置中添加一个 MultipartResolver 的实现来启用对 multipart 的支持,这样容器就会对每个请求进行检查,如果发现请求包含了 multipart 数据,就会使用配置的 MultipartResolver 实现来对请求进行处理。
MultipartResolver 的用法
下面例子演示了如何使用 Sping 自带的实现类 CommonsMultipartResolver 来启用 multipart 处理,在 Spring 应用上下文中添加如下配置:
1 2 3 4 5 6 7
| <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="100000"/> </bean>
|
添加了该配置,当 Spring 的 DispatcherServlet 收到包含 multipart 的请求后,配置的 MultipartResolver 就会被调用来处理这类请求,它会将当前的 HttpServletRequest 包装成一个 MultipartHttpServletRequest 来支持 multipart 的处理。从 MultipartHttpServletRequest 中你可以获取到 multipart 的数据内容,从而让你的 Controller 能够直接访问请求中包含的文件。
处理表单上传文件请求示例
假设我们创建的表单页面如下:
1 2 3 4 5 6 7 8 9 10
| <html> <head><title>File Upload</title></head> <body> <form method="post" action="upload.do" enctype="multipart/form-data"> <input type="text" name="fileName" /> <input type="file" name="fileData" /> <input type="submit" /> </form> </body> </html>
|
我们给 form 标签添加了 enctype 属性,其值为 multipart/form-data
,该属性是用来告知浏览器用什么方法对 multipart 字段内容进行编码。
相应的,我们创建一个 bean 来作为表单提交数据的容器(即MVC的Model):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class FileUpload { private String fileName; private CommonsMultipartFile fileData; public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public CommonsMultipartFile getFileData() { return fileData; } public void setFileData(CommonsMultipartFile fileData) { this.fileData = fileData; } }
|
可以看到这个 POJO 完全对应表单的两个字段,且对应上传的文件的字段是 CommonsMultipartFile 类型,它是 Jakarta Commons FileUpload 中 MultipartFile 的一个实现。
假设我们的 DispatcherServlet 配置如下:
1 2 3 4 5 6 7 8 9 10
| <servlet> <servlet-name>default</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
|
即所有发送到以 ‘.do’ 结尾的URL的请求都将由 default 这个 DispatcherServlet 进行处理转发。
接下来说明如何实现一个 Spring 的 Controller 来处理文件上传的请求。
首先来看 default-servlet.xml 的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> <context:component-scan base-package="sample.upload.controller" /> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="50000" /> </bean> </beans>
|
除了前面提到的 CommonsMultipartResolver 之外,我们添加了 context:component-scan 标签,并配置了其 base-package 属性。该标签可以让 Spring 自动扫描具有 Spring 注解的组件并载入上下文,而 base-package 就是需要扫描的包路径。
用来处理文件上传的 Controller 实现如下,省略了 imports 和一部分代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package sample.upload.controller; @Controller @RequestMapping(value="/upload.do") public class FileUploadController { @RequestMapping(method=RequestMethod.POST) public void upload(FileUpload fileUpload, BindingResult result) { if (result.hasErrors()) { StringBuilder info = new StringBuilder(); for (ObjectError error : result.getAllErrors()) { info.append("{").append(error.getCode()).append(": ") .append(error.getDefaultMessage()).append("} "); } logger.error(info.toString()); } if (logger.isDebugEnabled()) { logger.debug("File Name: " + fileUpload.getFileName()); if (fileUpload.getFileData() != null) { logger.debug("Original Filename: " + fileUpload.getFileData().getOriginalFilename()); logger.debug("File Type: " + fileUpload.getFileData().getContentType()); logger.debug("File Size: " + fileUpload.getFileData().getSize()); } } if (fileUpload.getFileData() != null && fileUpload.getFileData().getBytes() != null) { } } }
|
这个类通过添加 @Controller 注解将其声明为一个 Spring 的组件,这样我们前面配置的自动扫描就会自动将其载入上下文,我们实现了 upload 方法,并通过 @RequestMapping 注解使其处理通过 POST 方法发送到 upload.do 的 HTTP 请求,Spring 会自动解析请求中的参数,并为我们生成 FileUpload 这个 bean 的实例。CommonsMultipartFile 提供了多个 get… 方法可以让我们取得文件的相关信息和数据,接下来就可以根据自己的需要来处理上传的文件了。