SpringMVC文件下载和上传

1、文件下载

文件下载的最重要的一点是设置响应头的Content-dispositionattachmen;filename=要下载的文件的名字,然后得到文件的输入流写入本地即可
1. 常规方法

/**
 * 第一种文件下载的方法:使用原生的Servlet API
 *
 * @param filename 下载的文件名,SpringMVC会根据请求参数自动注入
 * @param request
 * @param response
 * @throws UnsupportedEncodingException
 */
@RequestMapping(value="/download1",method=RequestMethod.GET)
public void download(@ResuestParam("filename")String fileName, 
HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {

	response.setContentType("text/html;charset=utf-8");
	//解析文件在服务器中的真实路径
	String filePath = request.getServletContext().getRealPath("/download/" + fileName);
	//System.out.println(filePath);

	BufferedInputStream bis = null;
	BufferedOutputStream bos = null;

	try {

		//设置响应头告诉客户端浏览器,这个请求是要下载文件
		long fileLength = new File(filePath).length();
		response.setContentType("application/x-msdownload;");
		response.setHeader("Content-disposition", "attachment;filename=" + fileName);
		response.setHeader("Content-Length", String.valueOf(fileLength));

		//向客户端浏览器写文件数据
		bis = new BufferedInputStream(new FileInputStream(filePath));
		bos = new BufferedOutputStream(response.getOutputStream());
		byte[] buff = new byte[1024];
		int len;
		while (-1 != (len = bis.read(buff, 0, buff.length))) {
			bos.write(buff, 0, len);
		}
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		if (null != bis) {
			try {
				bis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if (null != bos) {
			try {
				bos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}

2. 使用SpringMVC提供的 ResponseEntity<T>类型,使用它可以很方便地定义返回的HttpHeaders和HttpStatus。

/**
 *第二种文件下载的方式:使用SpringMVC提供的esponseEntity类型
 * @param filename   下载的文件的名字,通过前端页面的请求参数带过来后SpringMVC会自动注入
 * @param request
 * @return
 * @throws IOException
 */
@RequestMapping(value = "/download2",method = RequestMethod.GET)
public ResponseEntity<byte[]> download(String filename, HttpServletRequest request) throws IOException {

	System.out.println(filename);
	//得到文件在服务器上的真实物理路径
	String filePath = request.getServletContext().getRealPath("/download/" + filename);
	/**
	 * 两个把下载文件转成byte[]的办法:一种是把要下载的文件封装成一个File对象,把这个对象交给FileCopyUtils.copyToByteArray(file)
	 */
	File file = new File(filePath);

	/**
	 * 另一种方法是:自己写一个转换的方法,如下
	 */
	/*FileInputStream fis = new FileInputStream(filePath);
	byte[] file = new byte[fis.available()];
	fis.read();
	fis.close();*/

	String downFileName = new String(filename.getBytes("utf-8"), "iso-8859-1");


	HttpHeaders headers = new HttpHeaders();
	//这是文件下载关键:设置contentDisposition为attachment
	headers.setContentDispositionFormData("attachment", downFileName);
	headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

	return new ResponseEntity<byte[]>(FileCopyUtils.copyToByteArray(file), headers, HttpStatus.OK);
}

下载页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
pageContext.setAttribute("ctp",request.getContextPath());
%>
<html>
<head>
<title>下载高清图片</title>
</head>
<body>
<h2>高清壁纸下载</h2>
<table>
<tr>
	<th>
		<img src="../..${ctp}/download/18.jpg" width="420px" height="320px"><br/>
		<button><a href="download1?filename=18.jpg">使用Servlet API下载</a></button>
		<button><a href="download2?filename=18.jpg">使用SpringMVC框架下载</a></button>
	</th>
	<th>
		<img src="../..${ctp}/download/13.jpg" width="420px" height="320px"><br/>
		<button><a href="download1?filename=13.jpg">使用Servlet API下载</a></button>
		<button><a href="download2?filename=13.jpg">使用SpringMVC框架下载</a></button>
	</th>
	<th>
		<img src="../..${ctp}/download/14.jpg" width="420px" height="320px"><br/>
		<button><a href="download1?filename=14.jpg">使用Servlet API下载</a></button>
		<button><a href="download2?filename=14.jpg">使用SpringMVC框架下载</a></button>
	</th>
</tr>
<tr>
	<th>
		<img src="../..${ctp}/download/15.jpg" width="420px" height="320px"><br/>
		<button><a href="download1?filename=15.jpg">使用Servlet API下载</a></button>
		<button><a href="download2?filename=15.jpg">使用SpringMVC框架下载</a></button>
	</th>
	<th>
		<img src="../..${ctp}/download/16.jpg" width="420px" height="320px"><br/>
		<button><a href="download1?filename=16.jpg">使用Servlet API下载</a></button>
		<button><a href="download2?filename=16.jpg">使用SpringMVC框架下载</a></button>
	</th>
	<th>
		<img src="../..${ctp}/download/17.jpg" width="420px" height="320px"><br/>
		<button><a href="download1?filename=17.jpg">使用Servlet API下载</a></button>
		<button><a href="download2?filename=17.jpg">使用SpringMVC框架下载</a></button>
	</th>
</tr>
</table>
</body>
</html>

测试结果


2、文件上传

文件上传这里使用的是commons-fileupload-1.4,他需要依赖commons-io,他们的Maven依赖如下:

<!--文件上传-->
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
	<groupId>commons-fileupload</groupId>
	<artifactId>commons-fileupload</artifactId>
	<version>1.4</version>
</dependency>

(1)单文件上传

  • 上传表单页面

  • 文件上传最重要的一点就是将表单的提交方法设置为post,并且将enctype的值设置为"multipart/form-data"。

    <!--单文件上传-->
    <h3>单文件上传</h3>
    <form action="${ctp}/uploadImg" method="post" enctype="multipart/form-data">
        头像: <input type="file" name="headerImg"/><br/>
        用户名:<input type="text" name="username"/><br/>
        <button type="submit">提交</button><br/>
    </form>
    

  • 控制器

  • 在Controller的处理方法中,使用MultipartFile对象作为参数接收前端上传过来的文件

    //单个文件上传
    @RequestMapping("/uploadImg")
    public String ImgUpload(
    		@RequestParam("username") String userName,
    		@RequestParam("headerImg") MultipartFile file,
    		HttpServletRequest request,
    		Model model) {
    
    	String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    	try {
    
    		//得到服务器上传文件的文件夹物理路径
    		String realPath = request.getServletContext().getRealPath("/upload/");
    		File dir=new File(realPath+date+"//");
    		if(!dir.exists()){
    			boolean res=dir.mkdir();
    			if(!res){
    				model.addAttribute("msg", "文件上传失败!请重试!");
    				return null;
    			}
    		}
    
    		//解析文件后缀名
    		String fileName = file.getOriginalFilename();
    		String suffix = fileName.substring(fileName.lastIndexOf("."), fileName.length());
    		if(".jpg".equals(suffix)||".png".equals(suffix)||".gif".equals(suffix)||".jpeg".equals(suffix)||"bmp".equals(suffix)) {
    			//给上传的文件重新命名
    			File newFileName = new File(dir.toString() + "//" + System.currentTimeMillis() + suffix);
    			//保存文件到服务器
    			file.transferTo(newFileName);
    			model.addAttribute("msg", "文件上传成功!");
    		}else{
    			model.addAttribute("msg","文件上传失败!只支持jpeg, jpg, png, gif, bmp 格式的图片文件");
    		}
    	} catch (Exception e) {
    		model.addAttribute("msg", "文件上传失败!");
    	}
    
    	return "fileUpLoad";
    }
    

  • 在springmvc配置文件中注册文件上传组件

  • 使用MultipartFile对象接收前端上传过来的文件,还需要在springmvc的配置文件中进行如下配置:

    <?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:context="http://www.springframework.org/schema/context"
          xmlns:mvc="http://www.springframework.org/schema/mvc"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd 
          http://www.springframework.org/schema/mvc
          http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
    <!--文件上传解析器的id是固定的,必须是multipartResolver-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    	<!--上传的文件总大小50M-->
    	<property name="maxUploadSize" value="#{1024*1024*50}"/>
    	<!--单个文件最大5M-->
    	<property name="maxUploadSizePerFile" value="#{1024*1024*5}"/>
    	<!--默认的字符编码:utf-8-->
    	<property name="defaultEncoding" value="UTF-8"/>
    </bean>
    </beans>
    

    (2)多文件上传
    其实多文件上传也很简单,单文件上传是在Controller的处理方法中使用MultipartFile对象作为参数接收前端上传过来的文件,而多文件上传则使用MultipartFile对象数组来接收。

  • 页面

  • 该页面中有几个name值一样的file类型的input标签,其他跟单文件上传的页面没区别。

    <h3>一次选一个文件,一次提交上传多个文件</h3>
    <form action="${ctp}/upload2" method="post" enctype="multipart/form-data">
        头像: <input type="file" name="headerImg"/><br/>
        图片: <input type="file" name="headerImg"/><br/>
        资料: <input type="file" name="headerImg"/><br/>
        文件: <input type="file" name="headerImg"/><br/>
        用户名:<input type="text" name="username"/><br/>
        <button type="submit">提交</button><br/>
    </form>
    

  • 控制器
  • 
    @Controller
    public class FileUpLoadController {
    
    @RequestMapping("/upload2")
    public String upload(@RequestParam("username") String userName,
    					 @RequestParam("headerImg") MultipartFile[] files,
    					 HttpServletRequest request,
    					 Model model) {
    	String realPath = request.getServletContext().getRealPath("/upload/");
    	File dir = new File(realPath + date +"//"+userName+"//");
    	if (!dir.exists()) {
    		boolean res = dir.mkdirs();
    		if (!res) {
    			model.addAttribute("msg", "文件上传失败!请重试!");
    			return null;
    		}
    	}
    
    	for (MultipartFile file : files) {
    		uploadFile(dir.toString(), file, model);
    	}
    
    	return "fileUpLoad";
    }
    
    public void uploadFile(String path, MultipartFile file, Model model) {
    	try {
    		if (!file.isEmpty()) {
    			String fileName = file.getOriginalFilename();
    			String suffix = fileName.substring(fileName.lastIndexOf("."), fileName.length());
    			//给上传的文件重新命名
    			File newFileName = new File(path + "//" + System.currentTimeMillis() + suffix);
    			System.out.println(newFileName);
    			//保存文件到服务器
    			file.transferTo(newFileName);
    			model.addAttribute("msg", "文件上传成功!");
    
    		}
    	} catch (Exception e) {
    		model.addAttribute("msg", "文件上传失败!" + e);
    	}
    }
    
    }
    

    同样的,使用MultipartFile数组接收前端上传过来的多个文件,也需要在springmvc的配置文件进行配置,具体配置与上述单文件上传的springmvc.xml配置没差别。这样,就可以进行多文件上传了。

    多种文件上传情景综合
    当然,项目开发中,场景可能并不是这么简单,上述的多文件上传是一个个文件选择后一起上传(即多个name相同的input标签),那要是我项目中只要一个input标签就可以一次性多个文件呢?又或者一个页面中既要一个个选择的多文件上传,又要一次性选择的多文件上传,还要有单文件上传呢?没问题,MultipartFile[]通吃,代码也很easy,下面直接上代码。

    页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%
       pageContext.setAttribute("ctp",request.getContextPath());
    %>
    <html>
    <head>
        <title>文件上传</title>
    </head>
    <body>
    <form action="${ctp}/upload3" method="post" enctype="multipart/form-data">
        <!--一次选择一个文件的多文件上传-->
        头像: <input type="file" name="headerImg"/><br/>
    
        <!--一次选择一个文件的多文件上传 -->
        <input type="file" name="img"/><br/>
        <input type="file" name="img"/><br/>
        <input type="file" name="img"/><br/>
    
        <!--一次选多个文件的多文件上传 -->
        图片:<input type="file" name="pic" multiple/><br/>
        用户名:<input type="text" name="username"/><br/>
        <button type="submit">提交</button><br/>
    </form>
    ${msg}
    </body>
    </html>
    

    控制器

    @RequestMapping("/upload3")
    public String upload(@RequestParam("username") String userName,
    					 @RequestParam("headerImg") MultipartFile[] files1,
    					 @RequestParam("img") MultipartFile[] files2,
    					 @RequestParam("pic") MultipartFile[] files3,
    					 HttpServletRequest request,
    					 Model model) {
    	String realPath = request.getServletContext().getRealPath("/upload/");
    	File dir = new File(realPath + date +"//"+userName+"//");
    	if (!dir.exists()) {
    		boolean res = dir.mkdirs();
    		if (!res) {
    			model.addAttribute("msg", "文件上传失败!请重试!");
    			return null;
    		}
    	}
    
    	for (MultipartFile file : files1) {
    		uploadFile(dir.toString(), file, model);
    	}
    
        for (MultipartFile file : files2) {
    		uploadFile(dir.toString(), file, model);
    	}
    
       	for (MultipartFile file : files3) {
    		uploadFile(dir.toString(), file, model);
    	}
    
    	return "fileUpLoad";
    }
    
    
    public void uploadFile(String path, MultipartFile file, Model model) {
    	try {
    		if (!file.isEmpty()) {
    			String fileName = file.getOriginalFilename();
    			String suffix = fileName.substring(fileName.lastIndexOf("."), fileName.length());
    			//给上传的文件重新命名
    			File newFileName = new File(path + "//" + System.currentTimeMillis() + suffix);
    			System.out.println(newFileName);
    			//保存文件到服务器
    			file.transferTo(newFileName);
    			model.addAttribute("msg", "文件上传成功!");
    
    		}
    	} catch (Exception e) {
    		model.addAttribute("msg", "文件上传失败!" + e);
    	}
    }
    

    测试结果

    MultipartFile[]就是如此强大,不管单个多个,逻辑处理一样,所以建议在项目开发中使用MultipartFile[]作为文件的接收参数。

    3、重要方法和参数

    1、MutipartFile类的一些常用方法:

    • String getContentType() //获取文件MIME类型
    • InputStream getInputStream() //获取文件流
    • String getName() //获取表单中文件组件的名字
    • String getOriginalFilename() //获取上传文件的原名
    • long getSize() //获取文件的字节大小,单位byte
    • boolean isEmpty() //是否为空
    • void transferTo(File dest) //保存文件到服务器指定路径

    2、CommonsMultipartResolver的属性解析

    • defaultEncoding:表示用来解析request请求的默认编码格式,当没有指定的时候根据Servlet规范会使用默认值ISO-8859-1。当request自己指明了它的编码格式的时候就会忽略这里指定的defaultEncoding。
    • uploadTempDir:设置上传文件时的临时目录,默认是Servlet容器的临时目录。
    • maxUploadSize:设置允许上传的总的最大文件大小,以字节为单位计算。当设为-1时表示无限制,默认是-1。
    • maxUploadSizePerFile:跟maxUploadSize差不多,不过maxUploadSizePerFile是限制每个上传文件的大小,而maxUploadSize是限制总的上传文件大小。
    • maxInMemorySize:设置在文件上传时允许写到内存中的最大值,以字节为单位计算,默认是10240。
    • resolveLazily:为true时,启用推迟文件解析,以便在UploadAction中捕获文件大小异常。

    留言区

    还能输入500个字符