Spring基础知识(19)- Spring MVC (九) | 文件上传、 文件下载
1. 文件上传
Spring MVC 框架的文件上传基于 commons-fileupload 组件,并在该组件上做了进一步的封装,简化了文件上传的代码实现,取消了不同上传组件上的编程差异。
1) MultipartResolver接口
在 Spring MVC 中实现文件上传十分容易,它为文件上传提供了直接支持,即 MultpartiResolver 接口。MultipartResolver 用于处理上传请求,将上传请求包装成可以直接获取文件的数据,从而方便操作。
MultpartiResolver 接口有以下两个实现类:
StandardServletMultipartResolver:使用了 Servlet 3.0 标准的上传方式。
CommonsMultipartResolver:使用了 Apache 的 commons-fileupload 来完成具体的上传操作。
MultpartiResolver 接口具有以下方法。
| 名称 | 作用 |
| byte[] getBytes() | 以字节数组的形式返回文件的内容 |
| String getContentType() | 返回文件的内容类型 |
| InputStream getInputStream() | 返回一个InputStream,从中读取文件的内容 |
| String getName() | 返回请求参数的名称 |
| String getOriginalFillename() | 返回客户端提交的原始文件名称 |
| long getSize() | 返回文件的大小,单位为字节 |
| boolean isEmpty() | 判断被上传文件是否为空 |
| void transferTo(File destination) | 将上传文件保存到目标目录下 |
2) 单文件上传
在 “Spring基础知识(12)- Spring MVC (二)” 的示例里,更新过 springmvc-beans.xml 的 SpringmvcBasic 项目基础上,修改如下。
(1) 导入 Apache Commons FileUpload 依赖包
访问 http://www.mvnrepository.com/,查询 commons-io、commons-fileupload
修改 pom.xml:
1 <project ... > 2 ... 3 4 <dependencies> 5 6 ... 7 8 <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> 9 <dependency> 10 <groupId>commons-io</groupId> 11 <artifactId>commons-io</artifactId> 12 <version>2.8.0</version> 13 </dependency> 14 <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> 15 <dependency> 16 <groupId>commons-fileupload</groupId> 17 <artifactId>commons-fileupload</artifactId> 18 <version>1.4</version> 19 </dependency> 20 21 ... 22 23 </dependencies> 24 25 ... 26 27 </project>
在IDE中项目列表 -> SrpingmvcBasic -> 点击鼠标右键 -> Maven -> Reload Project
(2) 修改 springmvc-beans.xml 文件,添加如下配置
1 <!-- 配置 MultipartResolver,用于上传文件,使用 spring 的 CommonsMultipartResolver --> 2 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> 3 <property name="maxUploadSize" value="5000000" /> 4 <property name="defaultEncoding" value="UTF-8" /> 5 </bean>
defaultEncoding:请求的编码格式,默认为 ISO-8859-1,此处设置为 UTF-8(注:defaultEncoding 必须和 JSP 中的 pageEncoding 一致,以便正确读取表单的内容)。
maxUploadSize:上传文件大小上限,单位为字节。
(3) 创建 src/main/java/com/example/entity/FileObject.java 文件
1 package com.example.entity; 2 3 import org.springframework.web.multipart.MultipartFile; 4 5 public class FileObject { 6 private MultipartFile file; 7 private String description; 8 9 public FileObject() { 10 11 } 12 13 public MultipartFile getFile() { 14 return file; 15 } 16 17 public void setFile(MultipartFile file) { 18 this.file = file; 19 } 20 21 public String getDescription() { 22 return description; 23 } 24 25 public void setDescription(String description) { 26 this.description = description; 27 } 28 }
(4) View
创建 src/main/webapp/WEB-INF/jsp/file.jsp 文件
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 5 <title>File</title> 6 </head> 7 <body> 8 <form action="${pageContext.request.contextPath }/file/post" method="POST" enctype="multipart/form-data"> 9 <p>File: <input type="file" name="file"></p> 10 <p>File Description: <input type="text" name="description"></p> 11 <p><input type="submit" value="Submit"></p> 12 </form> 13 </body> 14 </html>
创建 src/main/webapp/WEB-INF/jsp/showfile.jsp 文件
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 5 <title>Show File</title> 6 </head> 7 <body> 8 <p>File Description: ${fileObject.description }</p> 9 <!-- fileObject.getFile().getOriginalFilename()--> 10 <p>Filename:${fileObject.file().originalFilename }</p> 11 </body> 12 </html>
(5) 创建 src/main/java/com/example/controller/UploadController.java 文件
1 package com.example.controller; 2 3 import java.io.File; 4 import javax.servlet.http.HttpServletRequest; 5 import org.springframework.stereotype.Controller; 6 import org.springframework.web.bind.annotation.ModelAttribute; 7 import org.springframework.web.bind.annotation.RequestMapping; 8 9 import com.example.entity.FileObject; 10 11 @Controller 12 @RequestMapping("/upload") 13 public class UploadController { 14 15 @RequestMapping("/file") 16 public String file() { 17 return "file"; 18 } 19 20 @RequestMapping("/file/post") 21 public String filePost(@ModelAttribute FileObject fileObject, HttpServletRequest request) { 22 23 try { 24 String realpath = request.getServletContext().getRealPath("/uploads"); 25 File targetFile = new File(realpath, fileObject.getFile().getOriginalFilename()); 26 if (!targetFile.exists()) { 27 targetFile.mkdirs(); 28 } 29 30 fileObject.getFile().transferTo(targetFile); 31 } catch (Exception e) { 32 e.printStackTrace(); 33 } 34 35 return "showfile"; 36 } 37 }
访问:http://localhost:9090/upload/file
3) 多文件上传
在 “Spring基础知识(12)- Spring MVC (二)” 的示例里,更新过 springmvc-beans.xml 的 SpringmvcBasic 项目基础上,修改如下。
导入 Apache Commons FileUpload 依赖包和修改 springmvc-beans.xml 文件,参考上文 “单文件上传”。
(1) 导入 JSTL 依赖包
访问 http://www.mvnrepository.com/,查询 JSTL
修改 pom.xml:
1 <project ... > 2 3 <dependencies> 4 ... 5 6 <!-- https://mvnrepository.com/artifact/javax.servlet/jstl --> 7 <dependency> 8 <groupId>javax.servlet</groupId> 9 <artifactId>jstl</artifactId> 10 <version>1.2</version> 11 </dependency> 12 13 ... 14 </dependencies> 15 16 </project>
(2) 创建 src/main/java/com/example/entity/FileListObject.java 文件
1 package com.example.entity; 2 3 import java.util.List; 4 import org.springframework.web.multipart.MultipartFile; 5 6 public class FileListObject { 7 private List<String> descriptionList; 8 private List<MultipartFile> fileList; 9 10 public List<String> getDescriptionList() { 11 return descriptionList; 12 } 13 14 public void setDescriptionList(List<String> descriptionList) { 15 this.descriptionList = descriptionList; 16 } 17 18 public List<MultipartFile> getFileList() { 19 return fileList; 20 } 21 22 public void setFileList(List<MultipartFile> fileList) { 23 this.fileList = fileList; 24 } 25 }
(3) View
创建 src/main/webapp/WEB-INF/jsp/filelist.jsp 文件
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 5 <title>File List</title> 6 </head> 7 <body> 8 <form action="${pageContext.request.contextPath }/upload/filelist/post" method="post" enctype="multipart/form-data"> 9 <p>File 1:<input type="file" name="fileList"></p> 10 <p>File Description 1: <input type="text" name="descriptionList"></p> 11 <p>File 2: <input type="file" name="fileList"></p> 12 <p>File Description 2: <input type="text" name="descriptionList"></p> 13 <p><input type="submit" value="Submit"></p> 14 </form> 15 </body> 16 </html>
创建 src/main/webapp/WEB-INF/jsp/showfilelist.jsp 文件
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 2 <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 3 <html> 4 <head> 5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 6 <title>Show File List</title> 7 </head> 8 <body> 9 <table border="1px"> 10 <tr> 11 <td>Description</td> 12 <td>Filename</td> 13 </tr> 14 <!-- 同时取两个数组的元素 --> 15 <c:forEach items="${fileListObject.descriptionList}" var="description" varStatus="loop"> 16 <tr> 17 <td>${description}</td> 18 <td>${fileListObject.fileList[loop.count-1].originalFilename}</td> 19 </tr> 20 </c:forEach> 21 <!-- fileListObject.getFileList()[loop.count-1].getOriginalFilename() --> 22 </table> 23 </body> 24 </html>
(4) 创建 src/main/java/com/example/controller/UploadController.java 文件
1 package com.example.controller; 2 3 import java.util.List; 4 import java.io.File; 5 import javax.servlet.http.HttpServletRequest; 6 import org.springframework.stereotype.Controller; 7 import org.springframework.web.bind.annotation.ModelAttribute; 8 import org.springframework.web.bind.annotation.RequestMapping; 9 10 import org.springframework.web.multipart.MultipartFile; 11 import com.example.entity.FileListObject; 12 13 @Controller 14 @RequestMapping("/upload") 15 public class UploadController { 16 17 @RequestMapping("/filelist") 18 public String filelist() { 19 return "filelist"; 20 } 21 22 @RequestMapping("/filelist/post") 23 public String filelistPost(@ModelAttribute FileListObject fileListObject, HttpServletRequest request) { 24 25 String realpath = request.getServletContext().getRealPath("/uploads"); 26 File targetDir = new File(realpath); 27 if (!targetDir.exists()) { 28 targetDir.mkdirs(); 29 } 30 31 List<MultipartFile> files = fileListObject.getFileList(); 32 for (int i = 0; i < files.size(); i++) { 33 34 try { 35 File targetFile = new File(realpath, files.get(i).getOriginalFilename()); 36 files.get(i).transferTo(targetFile); 37 } catch (Exception e) { 38 e.printStackTrace(); 39 } 40 } 41 42 return "showfilelist"; 43 } 44 }
访问:http://localhost:9090/upload/filelist
2. 文件下载
文件下载就是将服务器中的文件下载到本地,下面主要介绍 Spring MVC 文件下载的实现方法和实现过程。
1) 通过超链接实现下载
实现简单,但暴露了下载文件的真实位置,并且只能下载 Web 应用程序所在目录下的文件,WEB-INF 目录除外。
2) 利用程序编码实现下载
增强安全访问控制,可以下载除 Web 应用程序所在目录以外的文件,也可以将文件保存到数据库中。
需要设置以下两个报头:
(1) Web 服务器需要告诉浏览器其所输出内容的类型不是普通文本文件或 HTML 文件,而是一个要保存到本地的下载文件,这需要设置 Content-Type 的值为 application/x-msdownload。
response.setHeader("Content-Type", "application/x-msdownload");
(2) Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。
response.setHeader("Content-Disposition", "attachment;filename="+filename);
该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有 attachment 是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename 参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。
分为两个步骤:
(1) 在客户端使用一个文件下载超链接,链接指向后台下载文件的方法以及文件名。
(2)) 在控制器类中,提供文件下载方法进行下载。
示例
在 “Spring基础知识(12)- Spring MVC (二)” 的示例里,更新过 springmvc-beans.xml 的 SpringmvcBasic 项目基础上,修改如下。
导入 JSTL 依赖包,参考上文 “多文件上传”。
(1) 创建 src/main/webapp/WEB-INF/jsp/download.jsp 文件
1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 2 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 5 <title>Download</title> 6 </head> 7 <body> 8 <p>Download files</p> 9 <!--遍历 model中的 files--> 10 <c:forEach items="${files}" var="filename"> 11 <p><a href="${pageContext.request.contextPath }/download/files?filename=${filename}">${filename}</a></p> 12 </c:forEach> 13 </body> 14 </html>
(2) 创建 src/main/java/com/example/controller/DownloadController.java 文件
1 package com.example.controller; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.UnsupportedEncodingException; 6 import java.util.ArrayList; 7 import javax.servlet.ServletOutputStream; 8 import javax.servlet.http.HttpServletRequest; 9 import javax.servlet.http.HttpServletResponse; 10 import org.springframework.stereotype.Controller; 11 import org.springframework.ui.Model; 12 import org.springframework.web.bind.annotation.RequestMapping; 13 import org.springframework.web.bind.annotation.RequestParam; 14 15 @Controller 16 @RequestMapping("/download") 17 public class DownloadController { 18 19 @RequestMapping("show") 20 public String show(HttpServletRequest request, Model model) { 21 22 String realpath = request.getServletContext().getRealPath("/uploads"); 23 File dir = new File(realpath); 24 File files[] = dir.listFiles(); 25 26 // 获取该目录下的所有文件名 27 ArrayList<String> fileName = new ArrayList<String>(); 28 for (int i = 0; i < files.length; i++) { 29 fileName.add(files[i].getName()); 30 } 31 32 model.addAttribute("files", fileName); 33 return "download.jsp"; 34 } 35 36 @RequestMapping("files") 37 public String files(@RequestParam String filename, 38 HttpServletRequest request, HttpServletResponse response) { 39 40 try { 41 42 String filePath = request.getServletContext().getRealPath("/uploads"); 43 44 // 设置下载文件使用的报头 45 response.setHeader("Content-Type", "application/x-msdownload"); 46 response.setHeader("Content-Disposition", "attachment; filename=" + toUTF8String(filename)); 47 48 // 读入文件 49 FileInputStream in = new FileInputStream(filePath + "/" + filename); 50 51 // 得到响应对象的输出流,用于向客户端输出二进制数据 52 ServletOutputStream out = response.getOutputStream(); 53 out.flush(); 54 55 int aRead = 0; 56 byte b[] = new byte[1024]; 57 while ((aRead = in.read(b)) != -1 & in != null) { 58 out.write(b, 0, aRead); 59 } 60 61 out.flush(); 62 in.close(); 63 out.close(); 64 } catch (Exception e) { 65 e.printStackTrace(); 66 } 67 68 return null; 69 } 70 71 /** 72 * 中文字符编码转换方法 73 */ 74 public String toUTF8String(String str) { 75 StringBuffer sb = new StringBuffer(); 76 int len = str.length(); 77 78 for (int i = 0; i < len; i++) { 79 // 取出字符中的每个字符 80 char c = str.charAt(i); 81 // Unicode码值为0~255时,不做处理 82 if (c >= 0 && c <= 255) { 83 sb.append(c); 84 } else { // 转换 UTF-8 编码 85 byte b[]; 86 try { 87 b = Character.toString(c).getBytes("UTF-8"); 88 } catch (UnsupportedEncodingException e) { 89 e.printStackTrace(); 90 b = null; 91 } 92 93 // 转换为%HH的字符串形式 94 for (int j = 0; j < b.length; j++) { 95 int k = b[j]; 96 if (k < 0) { 97 k &= 255; 98 } 99 sb.append("%" + Integer.toHexString(k).toUpperCase()); 100 } 101 } 102 } 103 104 return sb.toString(); 105 } 106 }
访问:http://localhost:9090/download/show
浙公网安备 33010602011771号