文件上传

Web 应用中,允许用户上传文件是很常见的需求。文件上传通常是采用 multipart 格式,而 DispatcherServlet 并没有任何解析 multipart 请求数据的功能,它将这个解析的任务委托给了 Spring 中的 MultipartResolver 策略接口的实现,通过这个实现类来解析 multipart 请求中的内容。

Spring 3.1 开始, Spring 内置了两个 MultipartResolver 实现供我们选择:

  • CommonsMultipartResolver: 使用 Jakarta Commons FileUpload 解析 multipart 请求
  • StandardServletMultipartResolver: 依赖于 Servlet 3.0multipart 请求的支持


使用 CommonsMultipartResolver

@Bean
public MultipartResolver multipartResolver() throws IOException {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
// 配置存放路径,默认存放在 Servlet 容器临时目录
multipartResolver.setUploadTempDir(new FileSystemResource("/tmp/spring/uploads"));
// 配置最大文件容量为 2MB
multipartResolver.setMaxUploadSize(2097152);
// 配置最大内存容量为0,即直接写入到硬盘中
multipartResolver.setMaxInMemorySize(0);
return multipartResolver;
}

使用 StandardServletMultipartResolver

相比于CommonsMultipartResolver 来说 StandardServletMultipartResolver 没有构造器和属性,所以它的申明会很简单:

@Bean
public MultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}

那么我们要如何取设置文件保存路径和大小呢?

我们可以在 Serlvet 中指定 multipart 的配置,我们之前通过继承 AbstractAnnotationConfigDispatcherServletInitializer 来实现了配置 DispatcherServlet,那么可以重载 customizeRegistration() 方法来配置 multipart:

@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
super.customizeRegistration(registration);
// 设置文件存放路径、文件最大文件容量为 2MB
// 整个请求不超过 4MB 并且直接写入到硬盘
registration.setMultipartConfig(
new MultipartConfigElement("/tmp/spring/uploads",2097152,
4194304,0));
}

可以看到 StandardServletMultipartResolver 相比于 CommonsMultipartResolver 多了一个设置整个请求大小的属性。


处理 multipart 请求

处理 multipart 请求有三种方式:

  • 原始的 byte[]
  • Spring 提供的 MultipartFile 接口
  • Servlet3 支持的 Part

**原始的 byte[] **

@RequestMapping("/register")
public String processRegistration(@RequestPart("profilePicture") byte[] profilePicture){
......
}

byte 数组形式不能提供给我们文件类型、文件原始名称等信息。


Spring 提供的 MultipartFile 接口

接口定义

public interface MultipartFile extends InputStreamSource {
String getName(); String getOriginalFilename(); String getContentType(); boolean isEmpty(); long getSize(); byte[] getBytes() throws IOException; InputStream getInputStream() throws IOException; void transferTo(File var1) throws IOException, IllegalStateException;
}

MultipartFile 提供了获取原始文件名称、大小、内容类型等信息,还提供了一个 transferTo 方法,帮助我们将上传的文件写入到文件系统中。


** Servlet3 支持的 Part**

接口定义:

public interface Part {

    InputStream getInputStream() throws IOException;

    String getContentType();

    String getName();

	String getSubmittedFileName();

    long getSize();

	void write(String fileName) throws IOException;

	void delete() throws IOException;

	String getHeader(String name);

	Collection<String> getHeaders(String name);

	Collection<String> getHeaderNames();
}

PartMultipartFile 基本差别不大,要注意的是如果使用了 *Part 那么就不需要配置 MultipartResolver 了。


异常处理

Spring 提供了多种方式将异常转为响应:

  • 特定的 Spring 异常将会自动映射为指定的 HTTP 状态码
  • 异常上可以添加 @ResponseStatus 注解,从而将其映射为某一个 HTTP 状态码
  • 在方法上添加 @ExceptionHandler 注解,使其用来处理异常

将异常映射为 HTTP 状态码

Spring 异常 状态码
BindException 400-Bad Request
ConversionNotSupportedException 500-Intemal Server Error
HttpMediaTypeNotAcceptableException 406-Not Acceptable
HttpMediaTypeNotSupportedException 415-Unsupported Media Type
HttpMessageNotReadableException 400-Bad Request
HttpMessageNotWritableException 500-Intemal Server Error
HttpRequestMethodNotSupportedException 405-Method NOt Allowed
MethodArgumentNotValidException 400-Bad Request
MissingServletRequestParameterException 400-Bad Request
MissingServletRequestPartException 400-Bad Request
NoSuchRequestHandlingMethodException 404-Not Found
TypeMismatchException 400-Bad Request

上面表中列出的异常一般会由 Spring 自身抛出,如果是应用自身抛出的异常它们就无能为力了,我们可以通过 @ResponseStatus 注解,将异常映射为特定的状态码。

@ResponseStatus(value=HttpStatus.NOT_FOUND,reason="not found")
public class SpringNotFoundException extends RuntimeException{ }

如果控制器抛出 SpringNotFoundException 异常后,响应状态将会变为 404

异常处理方法

除了返回状态之外,我们还想返回异常包含的信息,我们可以通过 @ExceptionHandler 注解来处理异常信息。

@ExceptionHandler(SpringNotFoundException.class)
public String handleExceptionHandler(){
......
}

@ExceptionHandler 可以处理同一个 Controller 中的异常,但是如果是其他 Controller 的话就无能为力了。Spring 也考虑到了这个问题,为我们提供了 @ControllerAdvice 注解,它会捕获到所有的 Controller 异常。

@ControllerAdvice
public class AppWideExceptionHandler{
@ExceptionHandler(SpringNotFoundException.class)
public String handleExceptionHandler(){
......
}
}

Spring学习之旅(九)--SpringMVC高级技术的更多相关文章

  1. Spring学习之旅(六)--SpringMVC集成

    对大多数 Java 开发来说,基于 web 的应用程序是我们主要的关注点. Spring 也提供了对于 web 的支持,基于 MVC 模式的 Spring MVC 能够帮助我们灵活和松耦合的完成 we ...

  2. Spring学习之旅(八)--SpringMVC请求参数

    现在我们已经完成了一个无参的接口了,但是应用中有很多需要携带参数的场景,我们来看看 ** SpringMVC** 对它的支持. 参数绑定 SpringMVC 提供了一种绑定机制,通过这个机制可以从请求 ...

  3. Spring学习之旅(七)--SpringMVC视图

    在之前的实例中我们只是在 Controller 中返回了 home 字符类型的值,而没有直接生成可以在浏览器中直接渲染的 HTML,这是因为 SpringMVC 将请求处理的逻辑和视图渲染的实现进行了 ...

  4. Spring学习之旅(十)--MockMvc

    在之前的 Spring学习之旅(八)--SpringMVC请求参数 我们是通过在控制台输出来验证参数是否正确,但是这样做实在是太耗时间了,我们今天来学习下 MockMvc,它可以让我们不需要启动项目就 ...

  5. Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探

    由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...

  6. 【Spring学习笔记-MVC-9】SpringMVC数据格式化之日期转换@DateTimeFormat

    作者:ssslinppp       1. 摘要 本文主要讲解Spring mvc数据格式化的具体步骤: 并讲解前台日期格式如何转换为java对象: 在之前的文章<[Spring学习笔记-MVC ...

  7. 【Spring学习笔记-MVC-4】SpringMVC返回Json数据-方式2

    <Spring学习笔记-MVC>系列文章,讲解返回json数据的文章共有3篇,分别为: [Spring学习笔记-MVC-3]SpringMVC返回Json数据-方式1:http://www ...

  8. 【Spring学习笔记-MVC-3】SpringMVC返回Json数据-方式1

    <Spring学习笔记-MVC>系列文章,讲解返回json数据的文章共有3篇,分别为: [Spring学习笔记-MVC-3]SpringMVC返回Json数据-方式1:http://www ...

  9. spring-mvc高级技术

    Spring MVC高级技术包括但不限于web.xml配置.异常处理.跨重定向请求传递数据 1.web.xml文件的配置 <!DOCTYPE web-app PUBLIC "-//Su ...

随机推荐

  1. 《VR入门系列教程》之7---DK2和Crescent Bay

    The DK2     于2014年春,Oculus发布了第二代开发版头显设备,代号为DK2.与DK1相比,Oculus Rift DK2的外观有很大改进,并且轻了许多,体积仍然比较大,可以罩住大部分 ...

  2. Java集合Collention

    集合:又被称为容器,用来存储数据,他是一个对象,能够将具有相同性质的多个元素汇聚成一个整体,集合被用于存储,获取,操纵和传输聚合的数据. 这种数据结构称为集合 Collenction:他是集合的根接口 ...

  3. Angular JS 中 指令详解

    Angular JS的强大功能就在于其可以自定义很多指令,现在就指令做一下详细的剖析. 一个Angular js 指令(directive)需要指定一个唯一的名字(myDirective)和一个函数, ...

  4. Android Studio 'AIDL is missing' 且 不识别R文件

    最近刚开始用Android Studio,出的问题还真不少.昨天不知为何不能新建项目了,这两天重装了几次才搞定. 可又出了这个问题: 原因:Compile Sdk Version和Build Tool ...

  5. 【iOS】Apple Mach-O Linker Error Linker command failed with exit code 1

    又遇到了这个问题,貌似之前遇到过……如图所示: 解决方法寻找中………… 在 Stack Overflow 找到了解决方法,如下: 参考链接:Apple Mach-O Linker Error

  6. 利用dockerfile 安装一个nginx-1.14.1

    FROM docker.io/centos MAINTAINER jim 107420988@qq.com ENV TZ "Asia/Shanghai" #ENV TERM xte ...

  7. Mysql执行过程总结

    总分三个阶段:Sql的解析,执行和结果获取阶段. 如下图,展开相熟.

  8. 雪花算法【分布式ID问题】【刘新宇】

    分布式ID 1 方案选择 UUID UUID是通用唯一识别码(Universally Unique Identifier)的缩写,开放软件基金会(OSF)规范定义了包括网卡MAC地址.时间戳.名字空间 ...

  9. 5.源码分析---SOFARPC调用服务

    我们这一次来接着上一篇文章<4. 源码分析---SOFARPC服务端暴露>讲一下服务暴露之后被客户端调用之后服务端是怎么返回数据的. 示例我们还是和上篇文章一样使用一样的bolt协议来讲: ...

  10. ajax具体实现学习记录

    记录自己对ajax\的理解, 首先要明白ajax是为了解决什么问题,简单来讲就是为了局部刷新页面,而不刷新整个界面.就比如现在有一个实时热度的显示,它是不断变化的,所以你肯定要不停的从数据库当中获取热 ...