SpringMVC 文件上传
所需依赖

<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency>
传统方式上传文件
通用上传页
<h3> 文件上传 </h3> <form action="user/fileupload" method="post" enctype="multipart/form-data"> 选择文件:<input type="file" name="upload"/><br/> <input type="submit" value=" 上传文件 "/> </form>
从 request 中获取文件
@RequestMapping("/fileupload")
public String fileuoload1(HttpServletRequest req) throws Exception { StandardMultipartHttpServletRequest request = (StandardMultipartHttpServletRequest) req;
System.out.println(" 文件上传...");
// 使用 fileupload 组件完成文件上传
// 上传的位置 获取最大的目录 即 webapp/WebRoot 目录
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判断,该路径是否存在
File file = new File(path);
if (!file.exists()) {
// 创建该文件夹
file.mkdirs();
}
// 解析 request 对象,获取上传文件项
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解析 request
List<FileItem> items = upload.parseRequest(request);
// 遍历
for (FileItem item : items) {
// 进行判断,当前 item 对象是否是上传文件项
if (item.isFormField()) {
// 说明普通表单向
} else {
// 说明上传文件项
// 获取上传文件的名称
String filename = item.getName();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 完成文件上传
item.write(new File(path, filename));
// 删除临时文件(大于 10k 的都会生成临时文件 因此要删除)
item.delete();
}
}
return "success";
}SpringMVC 方式上传
文件解析器配置
springmvc.xml 配置文件解析器:
<!-- 配置文件解析器对象,要求 id 名称必须是 multipartResolver 不能改 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="10485760"/> </bean>
更多配置
<!-- 多部分文件上传 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="104857600" /> <property name="maxInMemorySize" value="4096" /> <property name="defaultEncoding" value="UTF-8"></property> </bean>
上述配置文件单位是 Byte,10M 就是 10x1024x1024 相乘的值
上传大小异常处理
<!-- 配置文件解析器对象 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="51200" /> </bean> <!-- 1. 在文件上传解析时发现异常,此时还没有进入到 Controller 方法中 --> <bean id="exceptionResolver" class= "org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <!-- 遇到 MaxUploadSizeExceededException 异常时,跳转到 error.jsp 页面 --> <prop key= "org.springframework.web.multipart.MaxUploadSizeExceededException">/error </prop> </props> </property> </bean>
上述即文件超过 50k 就跳转错误页面,报错就不会执行 Controller 里的代码了。
也可以自定义异常类,如下:
/**
* 自定义异常处理器类
*/
public class ExceptionHandler implements HandlerExceptionResolver{
/**
* 处理上传文件大小超过限制抛出的异常
*/
@Override
public ModelAndView resolveException(HttpServletRequest req,
HttpServletResponse res, Object ob,Exception ex) { ModelAndView mv=new ModelAndView();
// 判断异常类型,来跳转不同页面
if (ex instanceof MaxUploadSizeExceededException){
// 指定错误信息
mv.addObject("errormessage", " 上传文件过大 ");
// 设置跳转视图
mv.setViewName("userEdit");
return mv;
}
// 其他异常
return null;
}
}然后将其加入 springmvc.xml 中即可
<bean class="com.cheng.exception.ExceptionHandler" />
由于 Tomcat 问题,7.0 系列版本在上传大文件如果超过大小限制会直接页面连接断开。这时可以不用 SpringMVC 自带的解析器限制文件大小,而是通过代码实现。
配置拦截器
public class FileUploadInterceptor extends HandlerInterceptorAdapter {
private long maxSize;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 判断是否文件上传
if(request!=null && ServletFileUpload.isMultipartContent(request)) { ServletRequestContext ctx = new ServletRequestContext(request);
// 获取上传文件尺寸大小
long requestSize = ctx.contentLength();
if (requestSize > maxSize) {
// 当上传文件大小超过指定大小限制后,模拟抛出 MaxUploadSizeExceededException 异常
throw new MaxUploadSizeExceededException(maxSize);
}
}
return true;
}
public void setMaxSize(long maxSize) {
this.maxSize = maxSize;
}
}配置解析器
<!-- 配置文件上传类型解析器 multipartResolver--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" /> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="cn.cheng.controller.FileUploadInterceptor"> <!-- 设定限制的文件上传大小 --> <property name="maxSize" value="51200"/> </bean> </mvc:interceptor> </mvc:interceptors>
上传格式异常处理
第一种在 Controller 方法中限制,但每次都写太过于麻烦,第二种使用拦截器,下列是后者:
public class FileTypeInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)throws Exception {
boolean flag= true;
// 判断是否为文件上传请求
if (request instanceof MultipartHttpServletRequest) {
MultipartHttpServletRequest multipartRequest =
(MultipartHttpServletRequest) request;
Map<String, MultipartFile> files =
multipartRequest.getFileMap();
Iterator<String> iterator = files.keySet().iterator();
// 对多部件请求资源进行遍历
while (iterator.hasNext()) { String formKey = (String) iterator.next();
MultipartFile multipartFile =
multipartRequest.getFile(formKey);
String filename=multipartFile.getOriginalFilename();
// 判断是否为限制文件类型
if (! checkFile(filename)) {
// 限制文件类型,请求转发到原始请求页面,并携带错误提示信息
request.setAttribute("errormessage", " 不支持的文件类型!");
request.getRequestDispatcher("/WEB-INF/pages/typeerror.jsp")
.forward(request, response);
flag= false;
}
}
}
return flag;
}
/**
* 判断是否为允许的上传文件类型,true 表示允许
*/
private boolean checkFile(String fileName) {
// 设置允许上传文件类型
String suffixList = "jpg,gif,png,ico,bmp,jpeg";
// 获取文件后缀
String suffix = fileName.substring(fileName.lastIndexOf(".")
+ 1, fileName.length());
if (suffixList.contains(suffix.trim().toLowerCase())) {
return true;
}
return false;
}
}xml 配置文件配置拦截器,注意先后顺序
<mvc:interceptors> <mvc:interceptor> <!-- /** 表示所有 URL 和子 URL 路径 --> <mvc:mapping path="/**"/> <!-- 配置自定义的文件上传类型限制拦截器 --> <bean class="cn.cheng.controller.FileTypeInterceptor"/> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="cn.cheng.controller.FileUploadInterceptor"> <!-- 设定限制的文件上传大小 --> <property name="maxSize" value="512000000000"/> </bean> </mvc:interceptor> </mvc:interceptors>
这样无论传多大都不会错,但是格式不正确会被跳转到错误页面。
拦截器返回 Json 数据
很多时候经常用 ajax 与 json 进行数据交互,因此并不一定每次出错都是跳转页面,返回 json 也很简单,直接写在拦截器 response 对象中即可
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null ;
try{ JSONObject res = new JSONObject();
res.put("success","false");
res.put("msg"," 上传文件类型错误 ");
out = response.getWriter();
out.append(res.toString());
return false;
}
catch (Exception e){ e.printStackTrace();
response.sendError(500);
return false;
}当然我们也可将其封装成工具类
public class SendMsgUtil {
/**
* 将某个对象转换成 json 格式并发送到客户端
* @param response
* @param obj
* @throws Exception
*/
public static void sendJsonMessage(HttpServletResponse response, Object obj) throws Exception { response.setContentType("application/json; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(JSONObject.toJSONString(obj, SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteDateUseDateFormat));
writer.close();
response.flushBuffer();
}
}用 MultipartFile 的 transferTo
代码:
@RequestMapping("/fileupload2")
public String fileuoload2(HttpServletRequest request, MultipartFile upload) throws Exception { System.out.println("springmvc 文件上传...");
// 使用 fileupload 组件完成文件上传
// 上传的位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判断,该路径是否存在
File file = new File(path);
if (!file.exists()) {
// 创建该文件夹
file.mkdirs();
}
// 说明上传文件项
// 获取上传文件的名称
String filename = upload.getOriginalFilename();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 完成文件上传 new File 中做目录 又文件名
upload.transferTo(new File(path, filename));
return "success";
}其中用文件参数类型用 CommonsMultipartFile 亦可,但是这时必须给参数 @RequestParam("file")注解指定所传文件项!
CommonsMultipartFile 的 getFileItem()
@RequestMapping("/fileupload2")
public String fileuoload2(HttpServletRequest request, @RequestParam("upload") CommonsMultipartFile upload) throws Exception { System.out.println("springmvc 文件上传...");
// 使用 fileupload 组件完成文件上传
// 上传的位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判断,该路径是否存在
File file = new File(path);
if (!file.exists()) {
// 创建该文件夹
file.mkdirs();
}
// 说明上传文件项
// 获取上传文件的名称
String filename = upload.getOriginalFilename();
String uploadFileFileNameWithoutSpace = filename.replaceAll(" ", "");
File targetFile = new File(path + File.separator, uploadFileFileNameWithoutSpace);
// 判断目标文件存在就删除 其实只要保证文件名唯一就无需该操作
if (targetFile.exists()) { targetFile.delete();
}
upload.getFileItem().write(targetFile);
return "success";
}其中 File.separator 等效于分隔符 "/",当然也可以拼接这个分隔符,按常理说 windows 系统路径分隔符是 \,使用时候需转义为\\,Linux 是/,因此 separator 可以对系统进行判断返回相应分隔符。
多文件上传
@RequestMapping("fileupload22")
public String springUpload(HttpServletRequest request) throws IllegalStateException, IOException { long startTime = System.currentTimeMillis();
// 将当前上下文初始化给 CommonsMutipartResolver (多部分解析器)
// 上传的位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver( request.getSession().getServletContext());
// 检查 form 中是否有 enctype="multipart/form-data"
if (multipartResolver.isMultipart(request)) {
// 将 request 变成多部分 request
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
// 获取 multiRequest 中所有的文件名
Iterator iter = multiRequest.getFileNames();
while (iter.hasNext()) {
// 一次遍历所有文件
MultipartFile file = multiRequest.getFile(iter.next().toString());
if (file != null) { String filepath = path + "/" + file.getOriginalFilename();
System.out.println(filepath);
// 上传
file.transferTo(new File(filepath));
}
}
}
long endTime = System.currentTimeMillis();
System.out.println(" 运行时间:" + (endTime - startTime) + "ms");
return "success";
}跨服务器上传
有专门的图片服务器 B,用户访问网站在 A 服务器,在 A 网站上传图片被转存到 B 服务器,A 服务器不作储存。首先我们需要两个 tomcat。
依赖:
<dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-core</artifactId> <version>1.18.1</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-client</artifactId> <version>1.18.1</version> </dependency>
实例代码
创建端口不同的 tomcat 的 web 项目,在 WEB-INF 里建 uploads 文件夹,修改 web.xml 配置文件
<web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>readonly</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> </web-app>
代码:
@RequestMapping("/fileupload3")
public String fileuoload3(MultipartFile upload) throws Exception { System.out.println(" 跨服务器文件上传...");
// 定义上传文件服务器路径
String path = "http://localhost:9090/uploads/";
// 说明上传文件项
// 获取上传文件的名称
String filename = upload.getOriginalFilename();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 创建客户端的对象
Client client = Client.create();
// 需要对文件名进行 URL 编码 若直接中文或空格上传会报错
filename = URLEncoder.encode(filename, "utf-8");
// 和图片服务器进行连接
WebResource webResource = client.resource(path + filename);
// 上传文件
webResource.put(upload.getBytes());
return "success";
}关于报错
409 可能原因是编译好的 web 目录下没有相应的例如以上的 uploads 文件夹,你项目的 web 目录即使有 ploads 文件夹文件夹但没一个文件,编译好后会自动忽略创建该目录,可在源码 web 目录文件夹内放任意文件即可。
403 可能是 web.xml 未按上述配置写入权限,另外如果以上代码文件名未 URL 编码,中文文件名文件上传亦会产生 403 错误。
文件上传注意事项
1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于 WEB-INF 目录下。
2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。
3、为防止一个目录下面出现太多文件,要使用 hash 算法打散存储。
4、要限制上传文件的最大值。
5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
- 传统方式上传文件
- SpringMVC 方式上传
- 文件解析器配置
- 上传大小异常处理
- 上传格式异常处理
- 拦截器返回 Json 数据
- 用 MultipartFile 的 transferTo
- CommonsMultipartFile 的 getFileItem()
- 多文件上传
- 跨服务器上传
- 实例代码
- 关于报错
- 文件上传注意事项
- 传统方式上传文件
- SpringMVC 方式上传
- 文件解析器配置
- 上传大小异常处理
- 上传格式异常处理
- 拦截器返回 Json 数据
- 用 MultipartFile 的 transferTo
- CommonsMultipartFile 的 getFileItem()
- 多文件上传
- 跨服务器上传
- 实例代码
- 关于报错
- 文件上传注意事项