上一篇日志中,我们介绍了 DispatcherServlet 的基本配置
DispatcherServlet 简介及配置
我们实现了 AbstractAnnotationConfigDispatcherServletInitializer 的三个 abstract 方法,而实际上,AbstractAnnotationConfigDispatcherServletInitializer 还提供了很多其他方法,只要我们去复写这些方法,就可以对 Servlet 进行很多灵活的自定义工作
此前我们介绍过 HTTP 的 multipart/form-data 请求:
HTTP multipart/form-data 请求类型简介
通过 multipart/form-data 请求可以方便的实现客户端到服务端的文件上传服务,但是默认的 DispatcherServlet 并不支持 multipart/form-data 请求,需要手动配置来启用 multipart/form-data 请求
java configuration 配置
AbstractAnnotationConfigDispatcherServletInitializer 将 DispatcherServlet 注册到 Servlet 容器后,会生成一个 ServletRegistration.Dynamic 对象,并用这个对象调用 customizeRegistration 方法
通过复写 customizeRegistration 方法可以进行对 DispatcherServlet 进行额外的配置
package com.techlog.test.configuration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration;
/**
* DispatcherServlet 初始化配置
* Created by techlog on 2016/11/17.
*/
public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* @return Spring 应用上下文配置类
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
/**
* @return Servlet Web 上下文配置类
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
/**
* @return 匹配的 URL 模式
*/
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setMultipartConfig(new MultipartConfigElement("/tmp/techlog/uploads"));
}
}
这里我们调用 setMultipartConfig 方法启用了 multipart 请求的处理,并将 /tmp/techlog/uploads 目录设为了上传文件接收目录
通过 MultipartConfigElement 的不同构造器我们除上传路径这个必须参数外,还可以指定其他可选的限制条件:
- 上传文件的最大容量(Byte)
- 整个 multipart 请求的最大容量(Byte)
- 上传文件大小(Byte)大于某个阈值则写入磁盘,否则保存在内存中,默认为 0
web.xml 配置
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app 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_3_0.xsd"
version="3.0">
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<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/techlog/uploads</location>
<max-file-size>2097152</max-file-size>
<max-request-size>4194304</max-request-size>
</multipart-config>
</servlet>
</web-app>
DispatcherServlet 并没有实现任何解析 multipart 请求数据的功能,他将任务委托给了 Spring 中的 MultipartResolver 策略接口的实现,通过他的实现类来解析 multipart 请求中的内容
Spring3.1 提供了两个 MultipartResolver 的实现类:
- CommonsMultipartResolver -- 使用 Jakarta Commons FileUpload 解析 multipart 请求
- StandardServletMultipartResolver -- 依赖于 Spring3.0 对 multipart 请求的支持
StandardServletMultipartResolver
通常来说,StandardServletMultipartResolver 是优先选择的方案,他不需要依赖任何其他的项目
@Bean
public MultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
看上去非常简单,因为 StandardServletMultipartResolver 类没有属性和构造器
CommonsMultipartResolver
CommonsMultipartResolver 的优势在于他可以应用于 Spring3.0 及以下的版本中
@Bean
public MultipartResolver multipartResolver() throws IOException {
CommonsMultipartResolver reslover = new CommonsMultipartResolver();
reslover.setUploadTempDir(new FileSystemResource("/tmp/techlog/uploads"));
return reslover;
}
CommonsMultipartResolver 并不会使用 Servlet 所配置的临时目录,因此需要在创建 CommonsMultipartResolver Bean 时指定,如果没有指定,那么就会使用 Servlet 容器的临时目录
你也可以通过 CommonsMultipartResolver 对象的其他 set 方法设置最大上传文件大小等限制条件
TestController
package com.techlog.test.controller;
import com.techlog.test.entity.FormEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
/**
* 用于测试 Controller
* Created by techlog on 2016/11/17.
*/
@Controller
public class TestController {
@RequestMapping("/upload")
public String upload(@RequestPart("picture") byte[] picture, @Validated FormEntity entity) {
/* ... */
}
}
@RequestPart 注解绑定“multipart/data”数据到一个 byte 数组,而 @Validated 注解完成了表单其他参数的传递和校验
@RequestPart 注解使用非常方便,但是你却无从得知文件名、文件类型等信息
Spring 提供了 MultipartFile 接口实现更加强大的文件获取功能,只要将此前 @RequestPart 注解的参数换成 MultipartFile 对象即可
package com.techlog.test.controller;
import com.techlog.test.entity.FormEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
/**
* 用于测试 Controller
* Created by techlog on 2016/11/17.
*/
@Controller
public class TestController {
@RequestMapping("/upload")
public String upload(@RequestPart("picture") MultipartFile picture, @Validated FormEntity entity) {
/* ... */
}
}
package org.springframework.web.multipart;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.core.io.InputStreamSource;
public interface MultipartFile extends InputStreamSource {
String getName();
String getOriginalFilename();
String getContentType();
boolean isEmpty();
long getSize();
byte[] getBytes() throws IOException;
@Override
InputStream getInputStream() throws IOException;
void transferTo(File dest) throws IOException, IllegalStateException;
}
可以看到,MultipartFile 接口除提供了 getBytes 方法获取文件的 byte 数组,还提供了 getName、getOriginalFilename、getContentType、getSize 等方法获取文件的基本信息
同时,通过 transferTo 方法可以将文件直接写入到文件系统中
Part 对象是另一个可选的 Controller 参数
package com.techlog.test.controller;
import com.techlog.test.entity.FormEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
/**
* 用于测试 Controller
* Created by techlog on 2016/11/17.
*/
@Controller
public class TestController {
@RequestMapping("/upload")
public String upload(@RequestPart("picture") Part picture, @Validated FormEntity entity) {
/* ... */
}
}
Part 接口与 Multipart 接口几乎是一样的,如果使用 Part 作为参数,而不是使用 Multipart,那么就没有必要配置 MultipartResolver 了
package javax.servlet.http;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
public interface Part {
InputStream getInputStream() throws IOException;
String getContentType();
String getName();
String getSubmittedFileName();
long getSize();
void write(String var1) throws IOException;
void delete() throws IOException;
String getHeader(String var1);
Collection<String> getHeaders(String var1);
Collection<String> getHeaderNames();
}
欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤

技术帖
controller
mvc
file
文件
技术分享
upload
java
spring
request
springmvc
multipart