在Spring MVC中处理文件上传有两种方法:
(1)Apache Commons FileUpload元件
(2)利用Servlet 3.0及其更高版本的内置支持。如果要将应用程序部署到支持Servlet 3.0及其更高版本的容器中,只能使用这种方法。
无论选择哪一种方法,都要利用相同的API来处理已经上传的文件。
1.客户端编程
为了上传文件,必须将HTML表格的enctype属性值设为multipart/form-data,像下面这样:
表格中必须包含类型为file的一个input元素,它会显示成一个按钮,点击时,它会打开一个对话框,用来选择文件。
在HTML5之前,如果想要上传多个文件,必须使用多个文件input元素。但是,在HTML5中,通过在input元素中引入多个multiple属性,使得多个文件的上传变得更加简单。在HTML5中编写以下任意一行代码,便可生成一个按钮供选择多个文件:
type="file" name="fieldName" multiple/>
type="file" name="fieldName" multiple="multiple"/>
type="file" name="fieldName" multiple=""/>
2.MultipartFile接口
在Spring MVC中处理已经上传的文件十分容易。上传到Spring MVC应用程序中的文件会被包在一个MultipartFile对象中。你唯一的任务就是用类型为MultipartFile的属性编写一个domain类。
org.springframework.web.multipart.MultipartFile接口具有以下方法:
byte[] getBytes()
它以字节数组的形式返回文件的内容。
String getContentType()
它返回文件的内容类型。
InputStream getInputStream()
它返回一个InputStream,从中读取文件的内容。
String getName()
它以多部分的形式返回参数的名称。
String getOriginalFilename()
它返回客户端本地驱动器中的初始文件名。
long getSize()
它以字节为单位,返回文件的大小。
boolean isEmpty()
它表示被上传的文件是否为空。
void transferTo(File destination)
它将上传的文件保存到目标目录下。
3.用Commons FileUpload上传文件
需要在Spring MVC配置文件中定义multipartResolver bean。
id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="2000000" />
4.Domain类
这个类具有类型为List
的images属性。
package app11a.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import org.springframework.web.multipart.MultipartFile;
import com.sun.istack.internal.NotNull;
public class Product implements Serializable
{
private static final long serialVersionUID = -164834188493691288L;
@NotNull
@Size(min = 1, max = 10)
private String name;
@NotNull
private String description;
private Float price;
@Past
private Date productionDate;
private List images;
...
5.控制器
保存已上传文件是一件很轻松的事情,只需要在MultipartFile中调用transferTo方法。
package app11a.controller;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import app11a.domain.Product;
@Controller
public class ProductController
{
private static final Log logger = LogFactory.getLog(ProductController.class);
@RequestMapping(value = "/product_input")
public String inputProduct(Model model)
{
model.addAttribute("product", new Product());
return "ProductForm";
}
@RequestMapping(value = "/product_save")
public String saveProduct(HttpServletRequest servletRequest,
@Valid @ModelAttribute Product product, BindingResult bindingResult,
Model model)
{
if (bindingResult.hasErrors())
{
FieldError fieldError = bindingResult.getFieldError();
logger.info("Code:" + fieldError.getCode() + ",field:" + fieldError.getField());
return "ProductForm";
}
List files = product.getImages();
List fileNames = new ArrayList();
if (null != files && files.size() > 0)
{
for (MultipartFile multipartFile : files)
{
String fileName = multipartFile.getOriginalFilename();
fileNames.add(fileName);
File imageFile = new File(getPath(servletRequest), fileName);
try
{
multipartFile.transferTo(imageFile);
}
catch (IllegalStateException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
// save product here
model.addAttribute("product", product);
return "ProductDetails";
}
private String getPath(HttpServletRequest servletRequest)
{
String pathUrl = servletRequest.getServletContext().getRealPath("/image");
File path = new File(pathUrl);
if (!path.exists() || !path.isDirectory())
{
path.mkdir();
}
return pathUrl;
}
}
6.配置文件
利用multipartResolver bean的maxUploadSize属性,可以设置能够接受的最大文件容量。如果没有设置这个属性,则没有最大文件容量限制。文件容量没有设置限制,并不意味着可以上传任意大小的文件。上传过大的文件时要花很长的时间,这样会导致服务器超时。为了处理超大文件的问题,可以利用HTML 5 File API将文件切片,然后再分别上传这些文件。
9.用Servlet 3及其更高版本上传文件
有了Servlet 3,就不需要Commons FileUpload和Commons IO元件了。在Servlet 3及其以上版本的容器中进行服务器端文件上传的编程,是围绕着标注类型MultipartConfig和javax.servlet.http.Part接口进行的。处理已上传文件的Servlets必须以@MultipartConfig进行标注。
下列是可能在MultipartConfig标注类型中出现的属性,它们都是可选的:
maxFileSize:上传文件的最大容量,默认值为-1,表示没有限制。大于指定值的文件将会遭到拒绝。
maxRequestSize:表示多部分HTTP请求允许的最大容量,默认值为-1,表示没有限制。
location:表示在Part调用write方法时,要将已上传的文件保存到磁盘中的位置。
fileSizeThreshold:上传文件超出这个容量界限时,会被写入磁盘。
Spring MVC的DispatcherServlet处理大部分或者所有请求。令人遗憾的是,如果不修改源代码,将无法对Servlet进行标注。但值得庆幸的是,Servlet 3中有一种比较容易的方法,能使一个Servlet变成一个MultipartConfig Servlet,即给部署描述符(web.xml)中的Servlet声明赋值。以下代码与用@MultipartConfig给DispatcherServlet进行标注的效果一样。
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/springmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<multipart-config>
<max-file-size>20848820</max-file-size>
<max-request-size>418018841</max-request-size>
<file-size-threshold>1048576</file-size-threshold>
</multipart-config>
</servlet>
此外,还需要在Spring MVC配件文件中使用一个不同的多部分解析器,像下面这样:
id="multipartResolver"
class="org.springframework.web.multipart.support.StandardServletMultipartResolver" />