7.2.1 配置 multipart 解析器
DispatcherServlet 并没有实现任何解析 multipart 请求数据的功能。它将该任务委托给了 Spring 中 MultipartResolver 策略接口的实现,通过这个实现类来解析 multipart 请求中的内容。从 Spring 3.1 开 始,Spring 内置了两个 MultipartResolver 的实现供我们选择:
  • CommonsMultipartResolver:使用 Jakarta Commons FileUpload 解析 multipart 请求;
  • StandardServletMultipartResolver:依赖于 Servlet 3.0 对 multipart 请求的支持(始于 Spring 3.1)。
一般来讲,在这两者之 间,StandardServletMultipartResolver 可能会是优选的方案。它使用 Servlet 所提供的功能支持,并不需要依赖任何其他的项目。如果我们需要将应用部署到 Servlet 3.0 之前的容器中,或者还没有使用 Spring 3.1 或更高版本,那么可能就需要 CommonsMultipartResolver 了。
使用 Servlet 3.0 解析 multipart 请求
兼容 Servlet 3.0 的 StandardServletMultipartResolver 没有构造器参数,也没有要设置的属性。这样,在 Spring 应用上下文中,将其声明为 bean 就会非常简单,如下所示:
1
@Bean
2
public MultipartResolver multipartResolver() throws IOException {
3
return new StandardServletMultipartResolver();
4
}
Copied!
既然这个 @Bean 方法如此简单,你可能就会怀疑我们到底该如何限制 StandardServletMultipartResolver 的工作方式呢。如果我们想要限制用户上传文件的大小,该怎么实现?如果我们想要指定文件在上传时,临时写入目录在什么位置的话,该如何实现?因为没有属性和构造器参数,StandardServletMultipartResolver 的功能看起来似乎有些受限。
其实并不是这样,我们是有办法配置 StandardServletMultipartResolver 的限制条件的。只不过不是在 Spring 中配置StandardServletMultipart-Resolver, 而是要在 Servlet 中指定 multipart 的配置。至少,我们必须要指定在文件上传的过程中,所写入的临时文件路径。如果不设定这个最基本配置的话,StandardServletMultipartResolver 就无法正常工作。具体来讲,我们必须要在 web.xml 或 Servlet 初始化类中,将 multipart 的具体细节作为 DispatcherServlet 配置的一部分。
如果我们采用 Servlet 初始化类的方式来配置 DispatcherServlet 的话,这个初始化类应该已经实现了 WebApplicationInitializer,那我们可以在 Servlet registration 上调用 setMultipartConfig() 方法,传入一个 MultipartConfig-Element 实例。如下是最基本的 DispatcherServlet multipart 配置,它将临时路径设置为 /tmp/spittr/uploads
1
DispatcherServlet ds = new DispatchServlet();
2
Dynamic registration = context.addServlet("appServlet", ds);
3
registration.addMapping("/");
4
registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads"));
Copied!
如果我们配置 DispatcherServlet 的 Servlet 初始化类继承了 Abstract-AnnotationConfigDispatcherServletInitializer 或 AbstractDispatcher-ServletInitializer 的话,那么我们不会直接创建 DispatcherServlet 实例并将其注册到 Servlet 上下文中。这样的话,将不会有对 Dynamic Servlet registration 的引用供我们使用了。但是,我们可以通过重载customize-Registration() 方法(它会得到一个 Dynamic 作为参数)来配置 multipart 的具体细节:
1
@Override
2
protected void customizeRegistration(Dynamic registration) {
3
registration.setMultipartConfig(
4
new MultipartConfigElement("/tmp/spittr/uploads");
5
);
6
}
Copied!
到目前为止,我们所使用是只有一个参数的 MultipartConfigElement 构造器,这个参数指定的是文件系统中的一个绝对目录,上传文件将会临时写入该目录中。但是,我们还可以通过其他的构造器来限制上传文件的大小。除了临时路径的位 置,其他的构造器所能接受的参数如下:
  • 上传文件的最大容量(以字节为单位)。默认是没有限制的。
  • 整个 multipart 请求的最大容量(以字节为单位),不会关心有多少个 part 以及每个 part 的大小。默认是没有限制的。
  • 在上传的过程中,如果文件大小达到了一个指定最大容量(以字节为单位),将会写入到临时文件路径中。默认值为 0,也就是所有上传的文件都会写入到磁盘上。
例如,假设我们想限制文件的大小不超过 2MB,整个请求不超过 4MB,而且所有的文件都要写到磁盘中。下面的代码使用 MultipartConfigElement 设置了这些临界值:
1
@Override
2
protected void customizeRegistration(Dynamic registration) {
3
registration.setMultipartConfig(
4
new MultipartConfigElement("/tmp/spittr/uploads",
5
2097152, 4194304, 0);
6
);
7
}
Copied!
如果我们使用更为传统的 web.xml 来配置 MultipartConfigElement 的话,那么可以使用 <servlet> 中的 <multipart-config> 元素,如下所示:
1
<servlet>
2
<servlet-name>appServlet</servlet-name>
3
<servlet-class>
4
org.springframework.web.servlet.DispatchServlet
5
</servlet-class>
6
<load-on-startup>1</load-on-startup>
7
<multipart-config>
8
<location>/tmp/spittr/upload</location>
9
<max-file-size>2097152</max-file-size>
10
<max-request-size>4194304</max-request-size>
11
</multipart-config>
12
</servlet>
Copied!
<multipart-config> 的默认值与 MultipartConfigElement 相同。与 MultipartConfigElement 一样,必须要配置的是 <location>。
配置 Jakarta Commons FileUpload multipart 解析器
通常来讲,StandardServletMultipartResolver 会是最佳的选择,但是如果我们需要将应用部署到非 Servlet 3.0 的容器中,那么就得需要替代的方案。如果喜欢的话,我们可以编写自己的 MultipartResolver 实现。不过,除非想要在处理 multipart 请求的时候执行特定的逻辑,否则的话,没有必要这样做。Spring 内置了 CommonsMultipartResolver,可以作为 StandardServletMultipartResolver 的替代方案。
将 CommonsMultipartResolver 声明为 Spring bean 的最简单方式如下:
1
@Bean
2
public MultipartResolver multipartResolver() {
3
return new MultipartResolver();
4
}
Copied!
与 StandardServletMultipartResolver 有所不同,CommonsMultipart-Resolver 不会强制要求设置临时文件路径。默认情况下,这个路径就是 Servlet 容器的临时目录。不过,通过设置 uploadTempDir 属性,我们可以将其指定为一个不同的位置:
1
@Bean
2
public MultipartResolver multipartResolver() throws IOException {
3
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
4
multipartResolver.setUploadTempDir(
5
new FileSystemResource("/tmp/spittr/uploads");
6
);
7
return multipartResolver;
8
}
Copied!
实际上,我们可以按照相同的方式指定其他的 multipart 上传细节,也就是设置 CommonsMultipartResolver 的属性。例如,如下的配置就等价于我们在前文通过 MultipartConfigElement 所配置的 StandardServlet-MultipartResolver:
1
@Bean
2
public MultipartResolver multipartResolver() throws IOException {
3
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
4
multipartResolver.setUploadTempDir(
5
new FileSystemResource("/tmp/spittr/uploads"));
6
multipartResolver.setMaxUploadSize(2097152);
7
multipartResolver.setMaxInMemorySize(0);
8
return multipartResolver;
9
}
Copied!
在这里,我们将最大的文件容量设置为 2MB,最大的内存大小设置为 0 字节。这两个属性直接对应于 MultipartConfigElement 的第二个和第四个构造器参数,表明不能上传超过 2MB 的文件,并且不管文件的大小如何,所有的文件都会写到磁盘中。但是与 MultipartConfigElement 有所不同,我们无法设定 multipart 请求整体的最大容量。
Last modified 2yr ago
Copy link