您的位置:首页 > 编程语言 > Java开发

spring in action 4 7.2 Processing multipart form data

2016-12-04 15:54 239 查看
7.2 Processing multipart form data


普通的表单生成的请求中的参数都很简单,就是用&连起来的名-值对.这种方式适用于文本类型的表单提交,但是二进制数据,比如说图片上传,用这种方式提交就不太可靠了.这类表单被称之为multipart
form,这种表单将数据分割成不同的部分,每一部分都是一个独立的域,可以拥有自己的类型.

下面显示了multipart请求体的内容

------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="firstName"
Charles
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="lastName"
Xavier
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="email"
charles@xmen.com
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="username"
professorx
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="password"
letmein01
------WebKitFormBoundaryqgkaBn8IHJCuNmiW
Content-Disposition: form-data; name="profilePicture"; filename="me.jpg"
Content-Type: image/jpeg
[[ Binary image data goes here ]]
------WebKitFormBoundaryqgkaBn8IHJCuNmiW--
在上面的multipart请求体中,profilePicture不同于其它部分.它有自己的Content-Type头信息表示自己是一个JPEG文件.虽然看上去并不明显,但是profilePicture的body是二进制数据而不是简单的文本.

虽然multipart请求看上去比较复杂,但是在spring mvc控制器中处理它们是比较简单的.不过在开始写对应的请求的控制器之前,需要先配置一个multipart解析器,让DispatchServlet能够识别DispatchServlet请求.

7.2.1 Configuring a multipart resolver

DispatchServlet本身并没有包含如何解析multipart请求中数据的逻辑.它会将multipart请求委托给一个spring MultipartResolver实现,由对方解析multipart请求中的内容.从spring版本3.1开始,spring发布时即包含了两个MultipartResolver实现

1. CommonsMultipartResolver 使用Jakarta Commons
FileUpload解析multipart请求

2. StandardServletMultipartResolver 依赖servlet 3.0对multipart请求的支持

通常来说,应该优先选择StandardServletMultipartResolver. 因为它是servlet3.0容器原生支持的,不需要导入其它项目包.但是如果应用部署在servlet3.0之前的容器里,或者spring的版本是3.1以下,那还是选择CommonsMultipartResolver吧.

RESOLVING
MULTIPART REQUESTS WITH SERVLET3.0 使用servlet3.0解析multipart请求

StandardServletMultipartResolver的构造方法没有参数,也不需要额外设置其它的属性.因此声明它的时候很方便,如下所示

@Bean
public MultipartResolver multipartResolver() throws IOException {
return new StandardServletMultipartResolver();
}
如何对上传的文件大小进行限制,如何指定文件存储的路径? 实际上,这样的限制不能在StandardServletMultipartResolver上设置,而应该在servlet中配置multipart时设置. 至少需要指定文件的临时存储路径,否则StandardServletMultipartResolver无法工作.
更具体点,你必须在web.xml或者servlet初始化类中设置multipart选项,将其做为DispatchServlet配置的一部分.

根据配置multipart选项的不同根据配置dispatchservlet的方法而不同

1. 如果直接通过实现WebApplicationInitializer接口配置了dispatchServlet,那么可以调用setMultipartConfig()来配置multipart.

如下示

DispatcherServlet ds = new DispatcherServlet();
Dynamic registration = context.addServlet("appServlet", ds);
registration.addMapping("/");
registration.setMultipartConfig(
new MultipartConfigElement("/tmp/spittr/uploads"));


2. 如果是通过继承AbstractAnnotationConfigDispatcherServletInitializer或者AbstractDispatcherServletInitializer类来配置dispatchservlet,那么就没有创建dispatchservlet的实例也没有直接在servlet上下文中注册它.这样的话,Dynamic就没有直接的引用了.但是可以直接重写customizeRegistration方法,在方法内部配置multipart.

如下示

@Override
protected void customizeRegistration(Dynamic registration) {
registration.setMultipartConfig(
new MultipartConfigElement("/tmp/spittr/uploads"));
}
译注:应该是第二种用的比较多吧.

MultipartConfigElement的构造方法有几个参数可以用于设置上传文件的最大值,等等信息,具体如下

 The maximum size (in bytes) of any file uploaded. By default there is no limit.
 The maximum size (in bytes) of the entire multipart request, regardless of how
many parts or how big any of the parts are. By default there is no limit.
 The maximum size (in bytes) of a file that can be uploaded without being written to the temporary location. The default is 0, meaning that all uploaded files
will be written to disk.

比如下面的代码就限制上传的文件大小不能超过2MB,整个请求不能超过4MB,请求的所有内容都需要写入磁盘

@Override
protected void customizeRegistration(Dynamic registration) {
registration.setMultipartConfig(new MultipartConfigElement("c:/xxxx", 2097152, 4194304, 0));
}


完整的java配置

import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration.Dynamic;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}

@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}

@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}

@Override
protected void customizeRegistration(Dynamic registration) {
registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads", 2097152, 4194304, 0));
}

//	@Override
//	protected Filter[] getServletFilters() {
//		return new Filter[] { new MyFilter() };
//	}
}
完整的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/spring/root-context.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
<multipart-config>
<location>/tmp/spittr/uploads</location>
<max-file-size>2097152</max-file-size>
<max-request-size>4194304</max-request-size>
</multipart-config>
</servlet>
</web-app>


CONFIGURING AJAKARTACOMMONS
FILEUPLOAD MULTIPART RESOLVER

StandardServletMultipartResolver是最好的选择,但是只能在servlet3.0的容器里使用.如你所愿,可以自己写MultipartResolver接口的实现.但是除非是要在处理multipart请求中执行一些特定的需求,否则没必要自己实现MultipartResolver接口.spring发布时原生包含了CommonsMultipartResolver,作为StandardServletMultipartResolver之外的另一个选择.

声明CommonsMultipartResolver的方法如下

@Bean
public MultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
与配置StandardServletMultipartResolver时不同,不需要明确给CommonsMultipartResolver指定文件存储的临时目录.默认情况下,该目录就是servlet容器的临时目录.当然也可以通过设置uploadTempDir属性来指定一个不同的目录.

@Bean
public MultipartResolver multipartResolver() throws IOException {
CommonsMultipartResolver multipartResolver =
new CommonsMultipartResolver();
multipartResolver.setUploadTempDir(
new FileSystemResource("/tmp/spittr/uploads"));
return multipartResolver;
}
通过设置属性值的方式还可以设置CommonsMultipartResolver的其它选项.比如说下面的设置与上面通过MultipartConfigElement设置StandardServletMultipartResolver实际的功能类似
@Bean
public MultipartResolver multipartResolver() throws IOException {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setUploadTempDir(new FileSystemResource("/tmp/spittr/uploads"));
multipartResolver.setMaxUploadSize(2097152);
multipartResolver.setMaxInMemorySize(0);
return multipartResolver;
}
从上面可见,有两个属性值与MultipartConfigElement的第二个和第四个构造方法参数值一致,表示上传的文件不能走过2MB,并且所有的文件都要写入磁盘.但是与MultipartConfigElement不同,multipartResolver没办法限制multipart请求的总大小.

7.2.2
Handling multipart requests

现在你已经成功配置了Spring对multipart请求的支持,现在需要编写相应的控制器方法以接收上传的文件了.实现该功能的最常用的方法就是给一个控制器方法加上@RequestPart的注解.

下面实现注册时让用户上传头像的功能.

先修改registrationForm.html文件,给个上传文件的入口.

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>
<%@ page session="false"%>
<html>
<head>
<title>Spittr</title>
<link rel="stylesheet" type="text/css"
href="<c:url value="/resources/style.css" />">
</head>
​<body>
<h1>Register</h1>
<sf:form method="POST" commandName="spitter" enctype="multipart/form-data">
<sf:errors path="*" element="div" cssClass="errors" />
First Name: <sf:input path="firstName" />
<!--             	<span id="firstName.errors">size must be between 2 and 30</span> -->
<br />
Last Name: <sf:input path="lastName" />
<!--             <span id="lastName.errors">size must be between 2 and 30</span> -->
<br />
Email: <sf:input path="email" />
<!--             <span id="email.errors">size must be between 2 and 30</span> -->
<br />
Username: <sf:input path="username" />
<!--             <span id="username.errors">size must be between 2 and 30</span> -->
<br />
Password: <sf:password path="password" />
<!--             <span id="password.errors">size must be between 2 and 30</span> -->
<br />
<label>Profile Picture</label>:
<input type="file"
name="profilePicture"
accept="image/jpeg,image/png,image/gif" />
<br/>
<input type="submit" value="Register" />
</sf:form>
</body>
</html>
控制器方法

//第7章
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String processRegistration(
@RequestPart("profilePicture") MultipartFile profilePicture,
@Valid Spitter spitter,
Errors errors) {
if (errors.hasErrors()) {
return "registerForm";
}
try {
//profilePicture.transferTo(new File("/data/spittr/" + profilePicture.getOriginalFilename()));
System.out.println(profilePicture.getOriginalFilename());
profilePicture.transferTo(new File("c:/" + profilePicture.getOriginalFilename()));
} catch (IllegalStateException | IOException e) {
e.printStackTrace();
}
//spitterRepository.save(spitter);
System.out.println(spitter.getEmail());
return "redirect:/spitter/" + spitter.getUsername();
}
这里只是输出了文件的名字,给上传的文件指定了目录,没有做太深入的设置

注意,方法中的参数设置:

@RequestPart("profilePicture") MultipartFile profilePicture
结果



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring 文件上传
相关文章推荐