SpringMVC的请求数据参数化的处理机制,使得上传中小型文件变得方便、快捷。在前端页面,与传统开发模式一样,使用<input type="file" name="file"/>标签来添加文件,同时为form表单设置:enctype="multipart/form-data" 的属性,当此类型的表单被提交后,SpringMVC会对multipart类型的数据进行解析。
1、MultipartFile类
在SpringMVC中,MultipartFile类主要用来接收并转换request请求中的multipart类型的文件数据。执行Controller获得MultipartFile类型的参数后,就可以使用该参数进行文件的处理了。
MultipartFile类的常用方法:
方法名 | 返回值 | 说明 |
getContentType() | String | 获取文件MIME类型。 |
getInputStream() | InputStream | 获取文件流。 |
getName() | String | 获取form表单中的文件组件的名字。 |
getOriginalFilename() | String | 获取上传文件的原名。 |
getSize() | long | 获取文件的大小,单位为byte。 |
isEmpty() | boolean | 判断文件是否为空。 |
transferTo(File dest) | void | 将数据保存到一个目标文件中。 |
2、实现文件的上传与下载
下面通过实现一个图片上传与下载的实例,来了解SpringMVC上传与下载文件的配置和操作,执行结果图如下:
2.1 配置multipart类型解析器
使用SpringMVC上传文件,首先需要在SpringMVC的核心配置文件springmvc.xml中配置multipart类型解析器,具体配置语句如下:
<!-- 多类型文件解析器、文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为1MB -->
<property name="maxUploadSize" value="1048576"/>
</bean>
注意:在该配置中,id="multipartResolver"属性是必须加上的,并且值是固定的。如果不加该id属性,则项目在运行时会报异常。
2.2 下载依赖的jar包
使用SpringMVC上传文件,其内部实现也使用Apache开源上传软件包fileupload与io包(如下图),所以要将这两个jar包的依赖引入工程中。下载地址如下:
如果使用Maven,则pom.xml文件配置如下:
<!-- 文件的上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
2.3 编写JSP页面
创建名为FileUpload.jsp的页面,编写上传图片的前端页面代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SpringMVC实现文件上传与下载</title>
<meta name="author" content="pan_junbiao的博客">
<style>
table { border-collapse: collapse; margin-bottom: 10px}
table,table tr th, table tr td { border:1px solid #000000; padding: 5px 10px;}
.txtBox{padding: 3px;width: 300px;font-size: 16px;}
</style>
</head>
<body>
<div align="center">
<form name="form1" action="${pageContext.request.contextPath}/file/uploadFile.action" method="post" enctype="multipart/form-data" onsubmit="return cheakSubmit()">
<table>
<caption>SpringMVC实现文件上传与下载</caption>
<tr>
<th>图片显示:</th>
<td><img src="${pageContext.request.contextPath}/UploadImages/${requestScope.newFileName}" width="100px" height="100px"/></td>
</tr>
<tr>
<th>上传文件:</th>
<td><input type="file" name="file"/></td>
</tr>
<tr>
<th>上传用户:</th>
<td><input class="txtBox" type="text" name="userName" value="pan_junbiao的博客"/></td>
</tr>
<tr>
<th>下载文件:</th>
<td><a href="${pageContext.request.contextPath}/file/downloadFile.action?fileName=myImage.jpg">下载文件</a></td>
</tr>
<tr>
<th>博客信息:</th>
<td>您好,欢迎访问 pan_junbiao的博客</td>
</tr>
<tr>
<th>博客地址:</th>
<td>;
</tr>
</table>
<input type="submit" value="上传"/>
<!-- 显示执行信息 -->
<span style="color:red"><%=request.getAttribute("message") == null ? "" : request.getAttribute("message")%></span>
</form>
</div>
</body>
<script>
//提交校验
function cheakSubmit()
{
var file = form1.file.value;
if (file == null||file == ""){
alert("请选择要上传的图片!");
return false;
}
//如果不存在"."
if (file.lastIndexOf('.')==-1){
alert("路径不正确!");
return false;
}
var fileExit=".jpg|.jpeg|.gif|.bmp|.png|";
//获取文件后缀名,并转换为小写
var suffix = file.substring(file.lastIndexOf(".")).toLowerCase();
if(fileExit.indexOf(suffix+"|")==-1)
{
alert("对不起!请上传图片!");
return false;
}
//校验成功
return true;
}
</script>
</html>
在该页面中,添加了一个包含enctype="multipart/form-data" 的属性的form表单,并且其中包含一个<input type="file" name="file"/>的文件上传标签。
2.4 编程Controller控制器
编写处理该上传与下载请求的Controller控制器类的方法。
package com.pjb.mvc.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 文件上传与下载控制器
* @author pan_junbiao
**/
@Controller
@RequestMapping("file")
public class FileController
{
@Autowired
private HttpServletRequest request;
@Autowired
private HttpServletResponse response;
//目录名称
private String _dirName = "/UploadImages/";
/**
* 上传文件
*/
@RequestMapping("uploadFile")
public String uploadFile(Model model, MultipartFile file, String userName)
{
try
{
if (file.isEmpty())
{
model.addAttribute("message", "请求选择要上传的文件");
return "/FileUpload.jsp";
}
//上传的图片的原始名称
String originalFilename = file.getOriginalFilename();
//判断文件大小
long size = file.getSize();
if (size > 1048576)
{
model.addAttribute("message", "文件大于1M,不能上传");
return "/FileUpload.jsp";
}
//获取文件后缀名,并转换为小写
String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
//只能上传图片,过滤不可上传的文件类型
String fileExit = ".jpg|.jpeg|.gif|.bmp|.png|";
if (fileExit.indexOf(suffix) <= -1)
{
model.addAttribute("message", "对不起!请上传图片");
return "/FileUpload.jsp";
}
//目录路径
String dirPath = request.getServletContext().getRealPath(_dirName);
//判断文件目录是否存在,不存在则创建
File dirFile = new File(dirPath);
if (!dirFile.exists())
{
dirFile.mkdirs();
}
//新的文件名称(格式为:yyyyMMddHHmmss+随机4位数+后缀名)
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); //设置日期格式
int randomNum = (int) (1000 + Math.random() * (9999 - 1000 + 1)); //随机4位数
String newFileName = df.format(new Date()) + randomNum + suffix;
//上传文件
File newFile = new File(dirPath+newFileName);
file.transferTo(newFile);
//返回成功
model.addAttribute("message", "文件上传成功");
model.addAttribute("newFileName", newFileName);
return "/FileUpload.jsp";
}
catch (Exception ex)
{
model.addAttribute("message", "文件上传失败");
return "/FileUpload.jsp";
}
}
/**
* 下载文件
*/
@RequestMapping("downloadFile")
public ResponseEntity<byte[]> downloadFile(String fileName)
{
try
{
if(fileName==null || fileName.length()==0)
{
return null;
}
request.setCharacterEncoding("UTF-8");
//目录路径
String dirPath = request.getServletContext().getRealPath(_dirName);
//获取文件
File file = new File(dirPath+fileName);
//判断文件是否存在
if(!file.exists())
{
return null;
}
//解决文件名乱码
String downloadFileName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
//读取二进制文件
byte[] body = null;
InputStream is = new FileInputStream(file);
body = new byte[is.available()];
is.read(body);
//通知浏览器以attachment(下载方式)打开图片
response.setHeader("Content-Disposition", "attchement;filename=" + downloadFileName);
//application/octet-stream二进制流数据(最常见的文件下载)。
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
//文件下载的Http协议中的状态最好使用HttpStatus.OK。
HttpStatus statusCode = HttpStatus.OK;
ResponseEntity<byte[]> entity = new ResponseEntity<byte[]>(body, statusCode);
return entity;
}
catch (Exception ex)
{
ex.toString();
return null;
}
}
}
3、上传多张图片
当需要上传多张图片时,可以在页面创建多个name相同的<input type="file" name="file"/>标签,这样就可以提交多张图片。或者利用移动端HTML5的特性,一个input一次性选择多张图片。多张图片的选择方式主要以前端设计为主,不过多介绍,这里主要介绍后台的处理逻辑,因为不论前端的图片选择模式如何,后台的上传处理逻辑是相同的。在处理图片上传的Controlller方法中,在参数中使用MultipartFile类的数组来接收相同name的文件资源,代码如下:
/**
* 上传多个文件
*/
@RequestMapping("uploadFiles")
public String uploadFiles(Model model, @RequestParam("files") MultipartFile[] files)
{
//利用数组的方式来处理多张图片,处理过程忽略...
}
注意:假设前端文件input的name为“files”,那么Controller方法中的MultipartFile数组的前面要添加@RequestParam("files")注解,表示解析所有名为files的文件资源,将其添加至MultipartFile数组(这里MultipartFile数组的名称仅为参数名称,可以任意命名),该注解不可以省略。