文件的上传和下载是项目开发中最常用的功能,例如图片的上传与下载、 邮件附件的上传与下载等。 接下来,本章将对 Spring MVC 环境中文件的上传和下载进行详细的讲解。
文件上传
- 文件上传概述
多数文件上传都是通过表单形式提交给后台服务器的,因此,要实现文件上传功能,就需要提供一个文件上传的表单,而该表单必须满足以下 3 个条件。
- form 表单的 method 属性设置为 post。
- form 表单的 enctype 属性设置为 multipart/form-data。
- 提供<input type="file" name="filename" />的文件上传输入框。
文件上传表单的示例代码如下。
<form action="uploadUrl" method="post" enctype="multipart/form-data"> <input type="file" name="filename"> <input type="submit" value="文件上传"> </form>
上述代码中,除了满足上传表单所必须的 3 个条件外,在<ínput>元素中还增加了一个 multiple 属性。 该属性是 HTML5 中的新属性,如果使用了该属性,则可以同时选择多个文件进行上传,即可实现多文件上传。
当客户端 form 表单的 enctype 属性为 multipart/form-data 时,浏览器就会采用二进制流的方式来处理表单数据,服务器端就会对文件上传的请求进行解析处理。 Spring MVC 为文件上传提供了直接的支持,这种支持是通过 MultipartResolver (多部件解析器)对象实现的 。 MultipartResolver 是一个接口对象,需要通过它的实现类 CommonsMultipartResolver 来完成文 件上传工作。 在 Spring MVC 中使用 MultipartResolver 对象非常简单,只需要在配置文件中定义 MultipartResolver 接口的 Bean 即可,其具体配置方式如下。<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 设置请求编码格式,必须与JSP中的pageEncoding属性一致 ,默认为ISO-8859-1 --> <property name="defaultEncoding" value="UTF-8" /> <!-- 设置允许上传文件的最大值(2MB),单位为字节 --> <property name="maxUploadSize" value="2097152" /> ... </bean>
在上述配置代码中,除配置了 CommonsMultipartResolver 类外,还通过<property>元素配置了编码格式以及允许上传文件的大小。 通过<property>元素可以对文件解析器类 CommonsMultipartResolver 的如下属性进行配置。
- maxUploadSize: 上传文件最大长度(以字节为单位)。
- maxlnMemorySize: 缓存中的最大尺寸。
- defaultEncoding: 默认编码格式。
- resolveLazily: 推迟文件解析,以便在 Controller 中捕获文件大小异常。
注意:因为 MultipartResolver 接口的实现类 CommonsMultipartResolver 内部是引用 multipartResolver 字符串获取该实现类对象并完成文件解析的,所以在配置 CommonsMultipartResolver 时,必须指定该 Bean 的 id 为MultipartResolver 。
由于 CommonsMultipartResolver 是 Spring MVC 内部通过 Apache Commons FileUpload 技术实现的,所以 Spring MVC 的文件上传还需要依赖 Apache Commons FileUpload 的组件, 即需要导入支持文件上传的相关 JAR 包,具体如下。
• commons-fileupload-1.3.2.jar
• commons-io-2.6.jar
以上两个 JAR 包大家可以通过 Apache 官网地址 " http://commons.apache.org/" 下载(进入该网址后,在 Apache Commons Proper 下方列表 的 Components 列中找到 FileUplod 和 iO ,单击链接后,即可在打开页面找到下载链接)。
当完成页面表单和文件上传解析器的配置后,在 Controller 中编写文件上传的方法即可实现 文件上传。 在 Spring MVC 中,文件上传的方法编写十分简单,其代码如下所示。@Controller public class FileUploadController { @RequestMapping("/fileUpload") public String handleFormUpload(@RequestParam("name") String name,@RequestParam("filename") MultipartFile file,...){ if(!file.isEmpty()){ //具体的执行办法 ... return "uploadSuccess"; } return "uploadFailure"; } }
在上述代码中,包含一个 MultipartFile 接口类型的参数 file ,上传到程序中的文件就是被封装在该参数中的。 org.springframework.web.multipart.MultipartFile 接口中提供了获取上传文件、 文件名称等方法,这些方法及其说明如表所示。
方法 说明 byte[] getBytes() 以字节数组的形式返回文件的内容 String getContentType() 返回文件的内窑类型 InputStream getlnputStream() 读取文内容,返回一个 InputStream 流 String getName() 获取多部件 form 表单的参数名称 String getOriginalFilename() 获取上传文件的初始化名 ong getSize() 获取上传文件的大小,单位是字节 boolean isEmpty() 判断上传的文件是否为空 void transferTo(File file) 将上传文件保存到目标目录下
- 应用案例一一文件上传
通过上一小节的学习,相信大家对 Spring MVC 中实现文件上传的步骤和配置已经有了一个 大致的了解。 接下来,本小节就通过一个具体的案例来演示文件上传功能的实现,其具体步骤如下。
( 1 )在 Eclipse 中创建一个名为 springmvc06 的 Web 项目,将 Spring MVC 相关 JAR 包以及支持文件上传下载的 JAR 包添加到项目的 lib 目录中,并发布到类路径下。 添加后的 lib 目录如图所示。
( 2 )在 web.xml 文件中,配置 Spring MVC 的前端控制器等信息。
( 3 )在 src 目录下,创建并编写 Spring MVC 的核心配置文件 springmvc-config.xml ,如文件所示。<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <!-- 定义组件扫描器,指定需要扫描的包 --> <context:component-scan base-package="com.neuedu.controller" /> <!-- 配置注解驱动 --> <mvc:annotation-driven /> <!-- 定义视图解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 设置前缀 --> <property name="prefix" value="/WEB-INF/jsp/" /> <!-- 设置后缀 --> <property name="suffix" value=".jsp" /> </bean> <!-- 配置文件上传解析器 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 设置请求编码格式 --> <property name="defaultEncoding" value="UTF-8" /> </bean> </beans>
在上述文件中 ,除配置了 Spring MVC 环境需要的组件扫描器、注解驱动和视图解析器外, 还增加了支持文件上传的解析器 CommonsMultipartResolver 的配置。
( 4 ) 在 WebContent 目录下 , 创建一个用于上传文件的页面 fileUpload.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>文件上传</title> <script> //判断是否填写上传人并已经选择上传文件 function check() { var name = document.getElementById("name").value; var file = document.getElementById("file").value; if(name == ""){ alert("请填写上传人!"); return false; } if(file.length==0||file==""){ alert("请选择上传文件!"); return false; } return true; } </script> </head> <body> <form action="fileUpload" method="post" enctype="multipart/form-data" onsubmit="return check()"> 上传人:<input type="text" id="name" name="name"><br /> 请选择文件:<input type="file" id="file" name="uploadfile"><br> <input type="submit" value="上传"> </form> </body> </html>
在上述文件中,编写了一个用于文件上传的 form 表单,该表单可以填写上传人并上传文件。 当单击"上传"按钮时,会先执行 check() 方法来检查上传人文本框和文件选择框中的内容是否为空。 只有填写了上传人并选择了需要上传的文件后,才能正常提交表单;否则表单将不会提交,并给出相应提示信息。 提交表单后,会以 POST 方式提交到一个以 "/fileUpload" 结尾的请求中。
( 5 )在 WEB-INF 目录下,创建 JSP 文件夹,并在文件夹中创建 success.jsp 和 error.jsp 文件,分别在两个文件的<body>元素内编写显示上传成功的信息(如"文件上传成功!" )和显示 上传失败的信息(如"文件上传失败,请重新上传!" )。
( 6 )在 src 目录下,创建一个 com.neuedu.controller 包,在该包下创建一个用于文件上传的控制器类 FileUploadController,编辑后文件如下所示。package com.neuedu.controller; import java.io.File; import java.io.IOException; import java.util.List; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; import com.sun.org.apache.bcel.internal.classfile.Field; /** * 文件上传 */ @Controller public class FileUploadController { /** * 执行文件上传 */ @RequestMapping("/fileUpload") public String handleFormUpload(@RequestParam("name") String name,@RequestParam("uploadfile") List<MultipartFile> uploadfile,HttpServletRequest request){ //判断所上传文件是否存在 if(!uploadfile.isEmpty() && uploadfile.size() > 0){ //循环输出上传的文件 for (MultipartFile file : uploadfile) { //获取上传文件的原始名称 String originalFilename = file.getOriginalFilename(); //设置上传文件的保存地址目录 String dirPath = request.getServletContext().getRealPath("/upload/"); File filePath = new File(dirPath); //如果保存文件的地址不存在,就先创建目录 if(!filePath.exists()){ filePath.mkdirs(); } //使用 UUID 重新命名上传的文件名称(上传人 _uuid_ 原始文件名称) String newFilename = name + "_"+UUID.randomUUID() + "_" + originalFilename; try { file.transferTo(new File(dirPath + newFilename)); } catch (Exception e) { e.printStackTrace(); return "error"; } } //跳转到成功页面 return "success"; }else{ return "error"; } } }
在文件中,使用注解方式定义了一个控制器类,并在类中定义了执行文件上传的方法 handleFormUpload()。 在 handleFormUpload() 方法参数中使用了List<MultipartFile>集合类型来接收用户上传的文件,然后判断所上传的文件是否存在。 如果存在,则继续执行上传操作,在通过 MultipartFile 接口的 transferTo() 方法将上传文件保存到用户指定的目录位置后,会跳转到 success.jsp 页面;如果文件不存在或者上传失败,则跳转到 error.jsp 页面。
( 7 )将项目发布到 Tomcat 服务器中并启动,在浏览器中访问地址 http://localhost8080/springmvc06/fileUpload.jsp ,其显示效果如图 16-2 所示。
在图的文件上传页面中,填写上传人并选择所要上传的文件,单击"上传"按钮后就可向后台发送上传请求信息。 这里填写上传人为"小韩",然后选择两张图片后,浏览器的显示效果如图所示。
单击"上传"按钮,程序在正确执行后浏览器就会跳转到 success.Jsp 页面,此时查看项目的发布目录,即可发现在 springmvc06 项目中多了一个 upload 文件夹,该文件夹中的内容如图所示。
从图中可以看出,已经成功上传了两张图片,并且图片的名称为"上传人uuid原始文件名称"的形式。 需要注意的是 , upload 文件夹是在项目的发布路径中,而不是创建的项目所在目录。 如果未更改项目发布路径,则要去工作空间的 metadata 目录中寻找项目发布目录(路径为: workspace.metadata.plugins\org.eclipse.WSt.server.core\tmpl\wtpwebapps\springmvc06\upload );如果将项目的发布路径已更改到 Tomcat 中,则需要在 Tomcat 的 webapps 目录中寻找项目 。
文件下载
- 实现文件下载
文件下载就是将文件服务器中的文件下载到本机上。 在 Spring MVC 环境中,实现文件下载大致可分为如下两个步骤。
( 1 )在客户端页面使用一个文件下载的超链接,该链接的 href 属性要指定后台文件下载的方法以及文件名(需要先在文件下载目录中添加了一个名称为 "1 .jpg" 的文件),具体代码示例如下。<a href="${pageContext.request.contextPath}/download?filename=1.jpg"> 文件下载 </a>
( 2 )在后台 Controller 类中,使用 Spring MVC 提供的文件下载方法进行文件下载。 Spring MVC 提供了一个 ResponseEntity 类型的对象,使用它可以很方便地定义返回的 HttpHeaders 对象和 HttpStatus 对象,通过对这两个对象的设置,即可完成下载文件时所需的配置信息。 文件下载的示例代码如下所示。
@RequestMapping("/download") public ResponseEntity<byte[]> fileDownload(HttpServletRequest request,String filename) throws IOException{ //指定要下载的文件所在路径 String path = request.getServletContext().getRealPath("/upload/"); //创建该文件对象 File file = new File(path + File.separator + filename); //设置响应头 HttpHeaders headers = new HttpHeaders(); //通知浏览器以下载的方式打开文件 headers.setContentDispositionFormData("attachment", filename); //定义以流的形式下载返回文件数据 headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); //使用Spring MVC框架的ResponseEntity对象封装返回下载数据 return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),headers,HttpStatus.OK); }
在 fileDownload() 方法中,首先根据文件路径和需要下载的文件名来创建文件对象,然后对响应头中文件下载时的打开方式以及下载方式进行了设置,最后返回 ResponseEntity 封装的下载结果对象。
ResponseEntity 对象有些类似前面章节介绍的@ResponseBody 注解,它用于直接返回结果对象。 上面示例中,设置响应头信息中的 MediaType 代表的是 Interner Media Type (即互联网媒体类型),也叫作 MIME 类型, MediaType.APPLlCATION_OCTET_STREAM 的值为 application/octet -stream ,即表示以二进制流的形式下载数据; HttpStatus 类型代表的是 Http 协议中的状态,示例中的 HttpStatus.OK 表示 200 ,即服务器已成功处理了请求。
在 Eclipse 的 WebContent 目录下,创建一个页面文件 download.jsp ,将上述的第 ( 1 ) 步的页面代码编写到 download.jsp 中,然后将第( 2 )步的 fileDownload() 方法编写在 FileUploadController 类中 。 发布项目并启动 Tomcat 服务器,在浏览器中访问地址 http://localhost:8080/springmvc06/download.jsp ,其显示效果如图所示。
单击图中的"文件下载"链接后,会出现下载提示弹窗,如图所示(这里以火狐浏览器为例进行演示)。
选择图中的"保存文件"并单击"确定"按钮后,即可下载该文件。
- 中文名称的文件下载
虽然在前面小节中,通过 Spring MVC 实现了文件下载功能,但此案例代码只适用于非中文名称的文件下载。 当对中文名称的文件进行下载时,因为各个浏览器内部转码机制的不同,就会出现不同的乱码以及解析异常问题。 例如在文件下载目录中添加一个名称为"壁纸.jpg" 的文件,当通过浏览器下载该文件时,下载弹出窗口的显示如图所示。
从图中可以看出,所要下载的文件名称并不是"壁纸.jpg" ,而是"_.jpg" ,这就表示中文文件名称出现了乱码。 那么我们要如何解决这种乱码问题呢? 为了解决浏览器中文件下载时中文名称的乱码问题,可以在前端页面发送请求前先对中文名进行统一编码,然后在后台控制器类中对文件名称进行相应的转码,其具体实现步骤如下。
( 1 )在下载页面中对中文文件名编码。 可以使用 Servlet API 中提供的 URLEncoder 类中的 encoder(String s, String enc)方法将中文转为 UTF-8 编码。 该方法中第一个参数表示需要转码的字符串,第二个参数表示编码格式,其具体实现方式如文件所示。<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ page import="java.net.URLEncoder"%> <!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>下载页面</title> </head> <body> <a href="${pageContext.request.contextPath}/download?filename=<%=URLEncoder.encode("壁纸.jpg","UTF-8")%>"> 中文名称文件下载 </a> </body> </html>
( 2 )修改控制器类 FileUploadController 中的 fileDownload() 方法,并增加对文件名进行编码的方法,其代码如下所示。
@RequestMapping("/download") public ResponseEntity<byte[]> fileDownload(HttpServletRequest request,String filename) throws IOException{ //指定要下载的文件所在路径 String path = request.getServletContext().getRealPath("/upload/"); //创建该文件对象 File file = new File(path + File.separator + filename); //对文件名编码,防止中文文件乱码 filename = this.getFilename(request,filename); //设置响应头 HttpHeaders headers = new HttpHeaders(); //通知浏览器以下载的方式打开文件 headers.setContentDispositionFormData("attachment", filename); //定义以流的形式下载返回文件数据 headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); //使用Spring MVC框架的ResponseEntity对象封装返回下载数据 return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),headers,HttpStatus.OK); } /** * 根据浏览器的不同进行编码设置,返回编码后的文件名 */ private String getFilename(HttpServletRequest request, String filename) throws UnsupportedEncodingException { //IE不同版本的User-Agent中出现的关键词 String[] IEBrowserKeyWords = {"MS1E", "Trident", "Edge"}; //获取请求头代理信息 String userAgent = request.getHeader("User-Agent"); for (String keyWord : IEBrowserKeyWords) { if(userAgent.contains(keyWord)){ //IE内核浏览器,统一为UTF-8编码显示 return URLEncoder.encode(filename, "UTF-8") ; } } //火狐等其他浏览器统一为ISO-8859-1编码显示 return new String(filename.getBytes("UTF-8"), "ISO-8859-1"); }
在方法 getFilename() 中,由于 IE 浏览器在文件编码上与其他浏览器的方式不同,所以在中文编码设置上 IE 浏览器设置为 UTF-8 编码,而火狐等其他浏览器设置为 ISO-8859-1 编码。 另外由于不同版本的 IE 浏览器,请求代理 User-Agent 中的关键字也略有不同,所以在判断 IE 浏览器时,需要特别注意 User-Agent 中的关键字。
再次进行中文名的文件下载测试,并在 IE 和火狐浏览器中分别单击文件下载链接后,两个浏览器的显示效果如图所示。
从图中的显示效果可以看出,所下载的文件已在两个浏览器中正确显示出了中文名称。
本章小结
本章主要对 Spring MVC 环境下的文件上传和下载进行了详细讲解。 首先讲解了如何实现文件上传,并通过一个应用案例来演示文件上传功能的实现;然后讲解了非中文名称文件下载的实现过程,以及中文名称文件下载的实现过程。 通过本章的学习,大家可以学会如何在 Spring MVC 环境下进行文件上传和下载,并能够掌握中文名称文件下载时乱码的解决方案。