概述
Servlet出现之前,文件上传的开发比较困难,主要涉及在服务端解析原始的HTTP响应。 03年Apache发布了开源的Commons FileUplaod组件,极大地提高了文件上传的开发效率。
经过很多年,Servlet的设计人员才意识到文件上传的重要性,最终文件上传还是成了Servlet3的内置特性。
因此,在SpringMVC中处理文件上传主要有两种方法
- 利用Apache Commons FileUpload组件
- 利用Servlet3.0及其更高版本的内置支持
本篇博文我们探讨利用Apache Commons FileUpload组件来实现文件的上传
客户端编程
为了上传文件,必须将HTML表格的enctype属性设置为 multipart/form-data
如下
<form action="action" enctype="multipart/form-data" method="post"> Select a file<input type="file" name="fileName"/> <input type="submit" value="Upload"/> </form>
表格中必须包含类型为file的一个input元素,它会显示成一个按钮,点击时会打开一个对话框,用来选择文件。
HTML之前,如果要上传多个文件,需要使用多个input元素。 但是HTML5中,通过input元素中一如多个multiple属性,使得多个文件的上传更加简单。 在HTML5中编写以下任意一行代码,遍可以生成一个按钮选择多个文件
<input type="file" name="fileName" multiple/> <input type="file" name="fileName" multiple="multiple"/> <input type="file" name="fileName" multiple=""/>
MultipartFile接口
上传到Spring MVC应用程序中的文件会被包装在一个MultipartFile对象中,我们仅仅用类型为MultipartFile的属性编写一个domain类即可
接口方法如下
利用Commons FileUpload上传文件
只有实现了Servlet3.0及其更高版本规范的Servlet容器,才支持文件上传。 对于低于Servlet3.0的容器,则需要使用Apache Commons FileUpload组件
maven依赖如下
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency>
我们使用1.3.1的版本
Domain类
我们的dimain类 Product,有一个类型为List< MultipartFile >的images属性
package com.artisan.domain; import java.io.Serializable; import java.util.List; import javax.validation.constraints.Size; import org.hibernate.validator.constraints.NotBlank; import org.springframework.web.multipart.MultipartFile; public class Product implements Serializable { private static final long serialVersionUID = -5379168879247929742L; @NotBlank @Size(min = 1, max = 10) private String name; private String description; private float price; //在Domain类中加入MultipartFile类型的属性,用来保存上传的文件 private List<MultipartFile> images; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public List<MultipartFile> getImages() { return images; } public void setImages(List<MultipartFile> images) { this.images = images; } }
控制器
控制器有两个处理请求的方法,inputProduct方法向浏览器返回一个产品表单,saveProduct方法将上传的文件保存到指定目录下
package com.artisan.controller; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.multipart.MultipartFile; import com.artisan.domain.Product; @Controller @RequestMapping("/product") public class ProductController { private static final Logger logger = Logger.getLogger(ProductController.class); @RequestMapping(value="/product_input",method=RequestMethod.GET) public String inputProduct(Model model){ model.addAttribute("product",new Product()); return "ProductForm"; } @RequestMapping(value="/product_save",method=RequestMethod.POST) public String saveProduct(HttpServletRequest servletRequest,@ModelAttribute Product product, BindingResult bindingResult, Model model) { List<MultipartFile> files = product.getImages(); List<String> fileNames = new ArrayList<String>(); if (null != files && files.size() > 0) { for (MultipartFile multipartFile : files) { String fileName = multipartFile.getOriginalFilename(); logger.info("fileName:" + fileName); fileNames.add(fileName); // 上传后的文件保存目录及名字 File imageFile = new File("D:/", fileName); try { // 转存文件 multipartFile.transferTo(imageFile); logger.info("save image successfully"); } catch (IOException e) { e.printStackTrace(); } } } // save product here model.addAttribute("product", product); return "ProductDetails"; } }
保存文件只需要在MultipartFile调用transferTo方法即可,非常便捷简单。
配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 扫描控制层的注解,使其成为Spring管理的Bean --> <context:component-scan base-package="com.artisan.controller" /> <!-- 静态资源文件 --> <mvc:annotation-driven /> <mvc:resources mapping="/css/**" location="/css/" /> <mvc:resources mapping="/*.jsp" location="/" /> <!-- 视图解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <!--1 CommonsMultipartResolver 第一个需要使用 Apache 的 commons-fileupload 等 jar 包支持, 但它可以在 servlet3.0以下的 版本中使用--> <!-- SpringMVC上传文件时,需要配置MultipartResolver处理器 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="2000000"/> <property name="defaultEncoding" value="utf-8"/> <property name="maxInMemorySize" value="40960"/> </bean> </beans>
利用multipartResolver bean中的各种属性设置合理的文件上传限制条件。
JSP页面
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE HTML> <html> <head> <title>Add Product Form</title> <style type="text/css">@import url("<c:url value="/css/main.css"/>");</style> </head> <body> <div id="global"> <form:form commandName="product" action="product_save" method="post" enctype="multipart/form-data"> <fieldset> <legend>Add a product</legend> <p> <label for="name">Product Name: </label> <form:input id="name" path="name" cssErrorClass="error"/> <form:errors path="name" cssClass="error"/> </p> <p> <label for="description">Description: </label> <form:input id="description" path="description"/> </p> <p> <label for="price">Price: </label> <form:input id="price" path="price" cssErrorClass="error"/> </p> <p> <label for="image">Product Image: </label> <input type="file" name="images[0]"/> </p> <p id="buttons"> <input id="reset" type="reset" tabindex="4"> <input id="submit" type="submit" tabindex="5" value="Add Product"> </p> </fieldset> </form:form> </div> </body> </html>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE HTML> <html> <head> <title>Save Product</title> <style type="text/css">@import url("<c:url value="/css/main.css"/>");</style> </head> <body> <div id="global"> <h4>The product has been saved.</h4> <p> <h5>Details:</h5> Product Name: ${product.name}<br/> Description: ${product.description}<br/> Price: $${product.price} <p>Following files are uploaded successfully.</p> <ol> <c:forEach items="${product.images}" var="image"> $ {image.originalFilename} </c:forEach> </ol> </p> </div> </body> </html>
部署及测试
看到目标目录D盘:
上传成功。
源码
代码已提交到github