1. 简介
Spring的内置多部件支持处理Web应用程序中的文件上传。可以使用org.springframework.web.multipart包中定义的可插入的MultipartResolver对象启用此多部件支持。 Spring提供了一个用于Commons FileUpload的MultipartResolver实现,另一个用于Servlet 3.0多部件请求解析。
默认情况下,Spring不进行多部件处理,因为一些开发人员希望自己处理多部件。可以通过向Web应用程序的上下文添加多部件解析器来启用Spring多部件处理。检查每个请求以查看它是否包含多部件。如果未找到任何多部件,则请求将按预期继续执行。如果在请求中找到了多部件,则将会使用已在上下文中声明的MultipartResolver。之后,请求中的multipart属性将被视为任意其他属性。
2. 将MultipartResolver与Commons FileUpload一起使用
以下示例显示如何使用CommonsMultipartResolver:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 最大文件大小,单位字节 -->
<property name="maxUploadSize" value="100000"/>
</bean>
当然,还需要在类路径中放置适当的jar,以使多部件解析器工作。对于CommonsMultipartResolver,需要使用commons-fileupload.jar。
当Spring DispatcherServlet检测到多部件请求时,它会激活已在上下文中声明的解析器并移交请求。解析器然后将当前的HttpServletRequest包装到支持多部分文件上传的MultipartHttpServletRequest中。使用MultipartHttpServletRequest,可以获取有关此请求包含的多部件的信息,并实际访问控制器中的多部件文件。
3. 将MultipartResolver与Servlet 3.0一起使用
为了使用基于Servlet 3.0的多部件解析,需要在web.xml中使用“multipart-config”部分标记DispatcherServlet,或在Servlet中通过代码方式使用javax.servlet.MultipartConfigElement注册,或者在自定义Servlet的类上有一个javax.servlet.annotation.MultipartConfig注解。由于Servlet 3.0不允许从MultipartResolver完成这些设置,因此需要在该Servlet注册级别应用配置设置(如最大大小或存储位置)。
一旦以上述方式之一启用了Servlet 3.0多部件解析,就可以将StandardServletMultipartResolver添加到Spring配置中:
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</bean>
4. 处理表单中的文件上传
MultipartResolver完成其任务后,将像处理其他任何请求一样处理该请求。 首先,创建一个带有文件输入的表单,允许用户上传表单。 encoding属性(enctype="multipart/form-data")让浏览器知道如何将表单编码为multipart请求:
<html>
<head>
<title>Upload a file please</title>
</head>
<body>
<h1>Please upload a file</h1>
<form method="post" action="/form" enctype="multipart/form-data">
<input type="text" name="name"/>
<input type="file" name="file"/>
<input type="submit"/>
</form>
</body>
</html>
下一步是创建一个处理文件上传的控制器。 这个控制器非常类似于普通的带注解的@Controller,除了我们在方法参数中使用MultipartHttpServletRequest或MultipartFile:
@Controller
public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(@RequestParam("name") String name, @RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
byte[] bytes = file.getBytes();
// 将bytes存储在某处,如数据库,本地文件
return "redirect:uploadSuccess";
}
return "redirect:uploadFailure";
}
}
注意@RequestParam方法参数如何映射到表单中声明的输入元素。 在此示例中,byte[]没有任何操作,但实际上可以将其保存在数据库中,将其存储在文件系统中,等等。
使用Servlet 3.0多部件解析时,还可以使用javax.servlet.http.Part作为方法参数:
@Controller
public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(@RequestParam("name") String name, @RequestParam("file") Part file) {
InputStream inputStream = file.getInputStream();
// store bytes from uploaded file somewhere
return "redirect:uploadSuccess";
}
}
5. 处理程序客户端的文件上传请求
还可以在RESTful服务方案中从非浏览器客户端提交多部件请求。所有上述示例和配置也适用于此处。但是,与通常提交文件和简单表单字段的浏览器不同,编程客户端还可以发送特定内容类型的更复杂数据 --- 例如,带有文件的多部件请求和带有JSON格式数据的第二部分:
POST /someUrl
Content-Type: multipart/mixed
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="meta-data"
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: 8bit
{
"name": "value"
}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="file-data"; filename="file.properties"
Content-Type: text/xml
Content-Transfer-Encoding: 8bit
... File Data ...
可以使用 @RequestParam("meta-data") String metadata 控制器方法参数访问名为“meta-data”的部分。但是,可能更愿意接受从请求部分体中的JSON格式数据初始化强类型对象,这与@RequestBody借助一个HttpMessageConverter将非多部件请求体转换为目标对象的方式非常相似。
为此,可以使用@RequestPart注解而不是@RequestParam注解。它允许通过HttpMessageConverter传递特定多部件的内容,同时考虑到multipart的'Content-Type'头:
@PostMapping("/someUrl")
public String onSubmit(@RequestPart("meta-data") MetaData metadata, @RequestPart("file-data") MultipartFile file) {
// ...
}
请注意如何使用@RequestParam或@RequestPart可以互换地访问MultipartFile方法参数。 但是,本案例中的@RequestPart("meta-data") MetaData 方法参数基于其“Content-Type”头读取为JSON内容,并在MappingJackson2HttpMessageConverter的帮助下进行转换。