SpringMVC 处理 multipart/data 请求实现文件上传

2016-11-24 17:54:39   最后更新: 2016-11-24 17:54:39   访问数量:439




上一篇日志中,我们介绍了 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 的不同构造器我们除上传路径这个必须参数外,还可以指定其他可选的限制条件:

  1. 上传文件的最大容量(Byte)
  2. 整个 multipart 请求的最大容量(Byte)
  3. 上传文件大小(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 的实现类:

  1. CommonsMultipartResolver -- 使用 Jakarta Commons FileUpload 解析 multipart 请求
  2. 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     


京ICP备15018585号