1.场景
实现单个文件上传和多个文件上传的功能,另外还可以预览上传到服务器上的图片,点击预览的某个图片,打开新的Tab看原图,如下图:
多文件上传,一次可以选择多个图片
单个文件上传,也就一次只能选择上传一张图片!
图片预览,这是我自己写的一个图片预览控件,感兴趣的话,可以看项目DEMO
点击预览的某张图片,打开新的Tab查看原图,如下
2.导入相关包
Sping MVC的文件上传功能依赖commons-fileupload.jar、commons-logging.jar、commons-lang.jar、commons-io.jar,另外上传的图片要进行预览的话,那就可能要用到jstl标签库的标签,进行for循环读取图片,所以就需要导入jstl.jar和standard.jar包,以及Spring MVC相关的jar包,如下图:
POM.xml添加的依赖包如下:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>3.8.1</junit.version>
<javax.servlet.version>2.5</javax.servlet.version>
<!-- 源代码Java编译版本 -->
<maven.compiler.source.version>1.7</maven.compiler.source.version>
<!-- 目标平台Java编译版本 -->
<maven.compiler.target.version>1.7</maven.compiler.target.version>
<spring.version>4.3.9.RELEASE</spring.version>
<commons.logging.version>1.2</commons.logging.version>
<javax.servlet.jsp.version>2.2</javax.servlet.jsp.version>
<jstl.jstl.version>1.2</jstl.jstl.version>
<taglibs.standard.version>1.1.2</taglibs.standard.version>
<commons-lang.version>2.6</commons-lang.version>
<commons-fileupload.commons-fileupload.version>1.3.1</commons-fileupload.commons-fileupload.version>
<org.slf4j.slf4j-log4j12.version>1.7.2</org.slf4j.slf4j-log4j12.version>
<jackson.version>2.5.4</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${javax.servlet.version}</version>
<scope>provided</scope> <!-- scope=provided 在编译和测试的过程有效,最后项目打包时不会加入 -->
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>${javax.servlet.jsp.version}</version>
<scope>provided</scope> <!-- scope=provided 在编译和测试的过程有效,最后项目打包时不会加入 -->
</dependency>
<!-- sl4j日志包 -->
<!--
slf4j-log4j12-版本号.jar 依赖以下两个包,也会自动添加进来
slf4j-api-版本号.jar
log4j-版本号.jar
例如:slf4j-log4j12-1.7.2.jar,就依赖slf4j-api-1.7.2.jar和log4j-1.2.17.jar这两个包
-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j.slf4j-log4j12.version}</version>
</dependency>
<!-- end -->
<!-- common包 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>${commons.logging.version}</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons-lang.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.commons-fileupload.version}</version>
</dependency>
<!-- end -->
<!-- JSTL标签库 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.jstl.version}</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>${taglibs.standard.version}</version>
</dependency>
<!-- end -->
<!-- JACKSON包,让Spring MVC支持JSON视图的解析以及返回JSON数据进行呈现 -->
<!--
jackson-databind-版本号.jar 依赖以下两个包,也会自动添加进来
jackson-annotations-版本号.jar
jackson-core-版本号.jar
例如:jackson-databind-2.5.4.jar,就依赖jackson-annotations-2.5.0.jar和jackson-core-2.5.4.jar这两个包
-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- end -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
3.Spring MVC配置文件上传解析器
因为要用到jsp页面上传文件,所以肯定也需要配置JSP视图解析器和静态资源处理,配置如下:
<!-- 静态资源处理, css, js, imgs,html 等-->
<mvc:resources location="/resources/" mapping="/resources/**" />
<!-- Jsp视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> -->
<property name="order" value="2" />
<property name="prefix" value="/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 文件上传解析器 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- maxUploadSize 文件附件的大小最大限制,maxUploadSize=-1 表示无穷大,单位是byte。本案例,限制的最大附件为50M -->
<property name="maxUploadSize" value="52428800" />
<property name="defaultEncoding" value="UTF-8" />
<!-- resolveLazily属性启用是为了推迟文件解析,以便捕获文件大小异常 -->
<property name="resolveLazily" value="true" />
</bean>
4、处理上传的文件的Controller
该案例Demo是做图片的上传和预览,所以Controller必须对上传的文件类型进行限制,只能上传图片!!
FileUploadController.java:
package edu.mvcdemo.controller;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import edu.mvcdemo.utils.FileComparator;
/**
* @编写人: yh.zeng
* @编写时间:2017-7-16 上午11:05:27
* @文件描述: 文件上传demo,通过代码控制,限制了只能上传图片
*/
@Controller
@Scope("singleton") //只实例化一个bean对象(即每次请求都使用同一个bean对象),默认是singleton
public class FileUploadController {
private Logger logger = LoggerFactory.getLogger(FileUploadController.class);
//从properties配置文件读取file.uploadpath的属性值并给字段赋值,默认值为E:\\360Downloads\\temp
@Value("${file.uploadpath:E:\\360Downloads\\temp}")
private String uploadpath;
//从properties配置文件读取character.encoding的属性值并给字段赋值,默认值为UTF-8
@Value("${character.encoding:UTF-8}")
private String characterEncoding;
/**
* 跳转至多文件上传页面
* @return
*/
@RequestMapping(value="/admin/mutilpartfile", method=RequestMethod.GET, params="upload")
private String showUploadPage(){
return "mutilpartfile-upload";
}
/**
* 跳转至单个文件上传页面
* @return
*/
@RequestMapping(value="/admin/onefile", method=RequestMethod.GET, params="upload")
private String showUploadPage2(HttpServletRequest request){
return "onefile-upload";
}
/**
* 单文件上传处理
* @param files
* @return
* @throws Exception
*/
@RequestMapping(value="/onefile/upload", method=RequestMethod.POST)
private String uploadOneFile(@RequestParam("file1") MultipartFile file) throws Exception {
String fileName = file.getOriginalFilename();
File targetDir = new File(uploadpath);
if (!targetDir.exists()) {
targetDir.mkdirs();
}
if (!fileName.toLowerCase().endsWith(".jpg")
&& !fileName.toLowerCase().endsWith(".png")
&& !fileName.toLowerCase().endsWith(".bmp")
&& !fileName.toLowerCase().endsWith(".gif")) {
throw new Exception("不是支持的文件类型!只支持图片上传!");
}
File targetFile = new File(uploadpath + File.separator + UUID.randomUUID() + "_" + fileName);
logger.info("上传图片:{}", targetFile.getPath());
file.transferTo(targetFile);
return "redirect:/uploaded-files/preview";
}
/**
* 多文件上传处理
* @param files
* @return
* @throws Exception
*/
@RequestMapping(value="/mutilpartfile/upload", method=RequestMethod.POST)
private String uploadMutilpartFile(@RequestParam("file1") MultipartFile files[]) throws Exception {
for(MultipartFile file : files){
String fileName = file.getOriginalFilename();
File targetDir = new File(uploadpath);
if(!targetDir.exists()){
targetDir.mkdirs();
}
if(!fileName.toLowerCase().endsWith(".jpg")
&& !fileName.toLowerCase().endsWith(".png")
&& !fileName.toLowerCase().endsWith(".bmp")
&& !fileName.toLowerCase().endsWith(".gif")
){
throw new Exception("不是支持的文件类型!只支持图片上传!");
}
File targetFile = new File(uploadpath + File.separator + UUID.randomUUID() + "_" + fileName);
logger.info("上传图片:{}", targetFile.getPath());
file.transferTo(targetFile);
}
return "redirect:/uploaded-files/preview";
}
/**
* 返回服务器上要预览的某张图片
* @param fileName
* @param fileType
* @param response
* @throws Exception
*/
@RequestMapping(value="/uploaded-files/preview/{fileType}/{fileName}", method=RequestMethod.GET)
private void previewImg(@PathVariable("fileName") String fileName, @PathVariable("fileType") String fileType,
HttpServletResponse response) throws Exception{
fileName = new String(fileName.getBytes("ISO-8859-1"), characterEncoding); //解决get方式中文乱码
logger.info("预览图片:{}{}{}.{}", uploadpath, File.separator, fileName, fileType);
BufferedImage image = ImageIO.read(new File(uploadpath + File.separator + fileName + "." + fileType));
switch(fileType.toLowerCase()){
case "jpg":
response.setContentType("image/jpeg");
break;
case "png":
response.setContentType("image/png");
break;
case "bmp":
response.setContentType("image/bmp");
break;
case "gif":
response.setContentType("image/gif");
break;
default:
throw new Exception("不是支持的文件类型!只支持预览图片!");
}
ImageIO.write(image, fileType , response.getOutputStream());
}
/**
* 预览已上传的所有图片
* @return
*/
@RequestMapping(value="/uploaded-files/preview")
private ModelAndView listUploadedImg(){
File dir = new File(uploadpath);
if(!dir.exists()){
dir.mkdirs();
}
//获取该文件夹下的图片
File fileArr[] = dir.listFiles(new FileFilter(){
@Override
public boolean accept(File file) {
String fileName = file.getName().toLowerCase();
if(fileName.endsWith(".jpg")
|| fileName.endsWith(".png")
|| fileName.endsWith(".bmp")
|| fileName.endsWith(".gif")
){
return true;
}else{
return false;
}
}
});
List<File> fileList = Arrays.asList(fileArr);
Collections.sort(fileList, new FileComparator());
String imgFileNames[] = new String[fileArr.length]; //文件名,去除后缀
String imgFileTypes[] = new String[fileArr.length]; //文件类型
for(int i = 0; i < fileArr.length; i++){
String fileName = fileArr[i].getName();
imgFileNames[i] = fileName.substring(0, fileName.lastIndexOf("."));
imgFileTypes[i] = fileName.substring(fileName.lastIndexOf(".")+1, fileName.length());
}
ModelAndView modelAndView = new ModelAndView("preview-uploaded-files");
modelAndView.addObject("imgFileNames", imgFileNames);
modelAndView.addObject("imgFileTypes", imgFileTypes);
return modelAndView;
}
}
本案例中,是将上传到服务器上的图片保存到磁盘中,所以做图片预览的时候,是从磁盘上取图片列表,返回到页面上展示。从磁盘上读取文件的代码,请看listUploadedImg方法。此外,在页面上展示图片的先后顺序肯定也要考虑的,这里就按照图片的最后修改时间进行降序排序(即最后上传的图片,展示在最前面)。
排序使用的是Collections.sort方法,如下
List<File> fileList = Arrays.asList(fileArr);
Collections.sort(fileList, new FileComparator());
FileComparator.java的代码如下:
package edu.mvcdemo.utils;
import java.io.File;
import java.util.Comparator;
/**
* @编写人: yh.zeng
* @编写时间:2017-7-20 上午12:55:34
* @文件描述: 文件列表按照最后修改时间降序排序
*/
public class FileComparator implements Comparator<File> {
@Override
public int compare(File file1, File file2) {
long time1 = file1.lastModified();
long time2 = file2.lastModified();
if(time1 == time2){
return 0;
}else if(time1 > time2){
return -1;
}else{
return 1;
}
}
}
5.文件上传的Jsp页面
由于该案例Demo是图片上传与预览的功能,所以页面上必须对上传的文件类型进行限制,只能上传图片!!
单个文件上传的页面onefile-upload.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>当个文件上传demo,本案例限制了只能上传图片</title>
<script type="text/javascript">
function checkFile(el){
var files = el.files; //获取选择的文件对象
var allowTypes = ["image/jpeg","image/png","image/x-png","image/bmp","image/gif"]; //允许上传的文件类型
var maxFileSize = 50 * 1024 * 1024; //允许上传的单个文件的大小限制,最大能上传50M
var allowUpload = true; //经过校验之后是否允许上传
var errorMessage = ""; //校验文件之后,文件不符合要求的提示信息
for(var i=0; i< files.length; i++){
var fileName = files[i].name; //文件名
var fileType = files[i].type; //文件类型
var fileSize = files[i].size; //文件大小,单位为byte(字节)
var typeAccepted = false;
for(var j = 0; j < allowTypes.length; j++){
if(allowTypes[j] == fileType){
typeAccepted = true;
break;
}
}
if(typeAccepted != true){
errorMessage += fileName + "不是图片,只能上传图片!";
allowUpload = false;
}
if(typeAccepted && fileSize > maxFileSize){
errorMessage += fileName+"的文件大小超出了50M限制!";
allowUpload = false;
}
}
if(allowUpload != true){
el.outerHTML = el.outerHTML; //清空选择的文件
alert(errorMessage);
}
}
function checkForm(){
if(document.getElementById("file1").value == ""){
alert("请选择要上传的文件!");
return false;
}
return true;
}
</script>
</head>
<body>
<form action="${pageContext.request.contextPath}/onefile/upload" method="post" enctype="multipart/form-data" onsubmit="return checkForm();">
<input type="file" name="file1" id="file1" onchange="checkFile(this)"/> <br>
<input type="submit" value="提交"/>
</form>
</body>
</html>
多个文件上传的页面mutilpartfile-upload.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>多文件上传demo,本案例限制了只能上传图片</title>
<script type="text/javascript">
function checkFile(el){
var files = el.files; //获取选择的文件对象
var allowTypes = ["image/jpeg","image/png","image/x-png","image/bmp","image/gif"]; //允许上传的文件类型
var maxFileSize = 50 * 1024 * 1024; //允许上传的单个文件的大小限制,最大能上传50M
var allowUpload = true; //经过校验之后是否允许上传
var errorMessage = ""; //校验文件之后,文件不符合要求的提示信息
for(var i=0; i< files.length; i++){
var fileName = files[i].name; //文件名
var fileType = files[i].type; //文件类型
var fileSize = files[i].size; //文件大小,单位为byte(字节)
var typeAccepted = false;
for(var j = 0; j < allowTypes.length; j++){
if(allowTypes[j] == fileType){
typeAccepted = true;
break;
}
}
if(typeAccepted != true){
errorMessage += fileName + "不是图片,只能上传图片!";
allowUpload = false;
}
if(typeAccepted && fileSize > maxFileSize){
errorMessage += fileName+"的文件大小超出了50M限制!";
allowUpload = false;
}
}
if(allowUpload != true){
el.outerHTML = el.outerHTML; //清空选择的文件
alert(errorMessage);
}
}
function checkForm(){
if(document.getElementById("file1").value == ""){
alert("请选择要上传的文件!");
return false;
}
return true;
}
</script>
</head>
<body>
<form action="${pageContext.request.contextPath}/mutilpartfile/upload" method="post" enctype="multipart/form-data" onsubmit="return checkForm();">
<input type="file" name="file1" id="file1" multiple="multiple" onchange="checkFile(this)"/> <br>
<input type="submit" value="提交"/>
</form>
</body>
</html>
6.文件预览的Jsp页面
preview-uploaded-files.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="image" uri="http://yh.zeng/tag/image" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>预览上传到服务器上的图片</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="${pageContext.request.contextPath}/resources/js/jquery-3.2.1.min.js"></script>
<style>
.imgBorder{
float: left;
border: 1px solid gray;
margin-top:5px;
margin-right: 5px;
padding: 10px;
color: gray;
font-size: 10px;
}
.imgClass{
cursor: pointer;
}
</style>
<script type="text/javascript">
$(function(){
$("img.imgClass").click(function(){
window.open($(this).attr("src"));
});
});
</script>
</head>
<body>
<c:forEach items="${requestScope.imgFileNames}" var="fileName" varStatus="i">
<div class="imgBorder">
${fileName}.${requestScope.imgFileTypes[i.index]}<br>
<image:scale src="${pageContext.request.contextPath}/uploaded-files/preview/${requestScope.imgFileTypes[i.index]}/${fileName}" styleClass="imgClass" width="300"/>
</div>
</c:forEach>
</body>
</html>
7.效果
单个文件上传
http://localhost:8080/MavenSpringMvcDemo/admin/onefile?upload
多个文件上传
http://localhost:8080/MavenSpringMvcDemo/admin/mutilpartfile?upload
上传到服务器上的图片预览,图片缩放的控件,是我自己写的控件,感兴趣的童鞋可以看项目DEMO
http://localhost:8080/MavenSpringMvcDemo/uploaded-files/preview
点击某个预览的图片,查看原图