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) 单文件上传

        在 “” 的示例里,更新过 springmvc-beans.xml 的 SpringmvcBasic 项目基础上,修改如下。

        (1) 导入 Apache Commons FileUpload 依赖包

            访问 http://www.mvnrepository.com/,查询 commons-io、commons-fileupload

            修改 pom.xml:

 1                 
 2                     ...
 3 
 4                     
 5 
 6                         ...
 7 
 8                         
 9                         
10                             commons-io
11                             commons-io
12                             2.8.0
13                         
14                         
15                         
16                             commons-fileupload
17                             commons-fileupload
18                             1.4
19                         
20 
21                         ...
22 
23                     
24                 
25                     ...    
26 
27                 


            在IDE中项目列表 -> SrpingmvcBasic -> 点击鼠标右键 -> Maven -> Reload Project

        (2) 修改 springmvc-beans.xml 文件,添加如下配置

1             
2             "multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
3                 "maxUploadSize" value="5000000" />
4                 "defaultEncoding" value="UTF-8" />
5             

            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                 
 3                 
 4                     "Content-Type" content="text/html; charset=UTF-8">
 5                     File
 6                 
 7                 
 8                     
"${pageContext.request.contextPath }/file/post" method="POST" enctype="multipart/form-data"> 9

File: "file" name="file">

10

File Description: "text" name="description">

11

"submit" value="Submit">

12
13 14


            创建 src/main/webapp/WEB-INF/jsp/showfile.jsp 文件

 1                 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
 2                 
 3                 
 4                     "Content-Type" content="text/html; charset=UTF-8">
 5                     Show File
 6                 
 7                 
 8                     

File Description: ${fileObject.description }

9 10

Filename:${fileObject.file().originalFilename }

11 12


        (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) 多文件上传

        在 “” 的示例里,更新过 springmvc-beans.xml 的 SpringmvcBasic 项目基础上,修改如下。

        导入 Apache Commons FileUpload 依赖包和修改 springmvc-beans.xml 文件,参考上文 “单文件上传”。

        (1) 导入 JSTL 依赖包

            访问 http://www.mvnrepository.com/,查询 JSTL
        
            修改 pom.xml:

 1             
 2 
 3                 
 4                     ...
 5 
 6                     
 7                     
 8                     javax.servlet
 9                     jstl
10                     1.2
11                     
12                     
13                     ...
14                 
15 
16             


        (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 descriptionList;
 8                 private List fileList;
 9 
10                 public List getDescriptionList() {
11                     return descriptionList;
12                 }
13 
14                 public void setDescriptionList(List descriptionList) {
15                     this.descriptionList = descriptionList;
16                 }
17 
18                 public List getFileList() {
19                     return fileList;
20                 }
21 
22                 public void setFileList(List 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                 
 3                 
 4                 "Content-Type" content="text/html; charset=UTF-8">
 5                 File List
 6                 
 7                 
 8                     
"${pageContext.request.contextPath }/upload/filelist/post" method="post" enctype="multipart/form-data"> 9

File 1:"file" name="fileList">

10

File Description 1: "text" name="descriptionList">

11

File 2: "file" name="fileList">

12

File Description 2: "text" name="descriptionList">

13

"submit" value="Submit">

14
15 16


            创建 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                 
 4                 
 5                     "Content-Type" content="text/html; charset=UTF-8">
 6                     Show File List
 7                 
 8                 
 9                     "1px">
101112131415"${fileListObject.descriptionList}" var="description" varStatus="loop">
16                             171819202122
Description Filename
${description} ${fileListObject.fileList[loop.count-1].originalFilename}
23 24


        (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 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)) 在控制器类中,提供文件下载方法进行下载。

    示例

        在 “” 的示例里,更新过 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             
 4                 "Content-Type" content="text/html; charset=UTF-8">
 5                 Download
 6             
 7             
 8                 

Download files

9 10 "${files}" var="filename"> 11

"${pageContext.request.contextPath }/download/files?filename=${filename}">${filename}

12
13 14


        (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 fileName = new ArrayList();
 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