第16章 文件上传和下载
学习目标
● 熟悉Spring MVC中文件上传的实现步骤
● 掌握文件上传案例的编写
● 掌握中英文名称文件下载程序的编写
16.1 文件上传
16.1.1 文件上传概述
· 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" multiple="multiple" />
<input type="submit" value="文件上传" /> </form>
multiple属性是HTML5中的新属性,如果使用了该属性,则可以同时选择多个文件进行上传,即可实现多文件上传。
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>
· maxInMemorySize:缓存中的最大尺寸。
· defaultEncoding:默认编码格式。
· resolveLazily:推迟文件解析,以便在Controller中捕获文件大小异常。
配置CommonsMultipartResolver时必须指定该Bean的id为multipartResolver
CommonsMultipartResolver是Spring MVC内部通过Apache Commons FileUpload实现的,即需要导入支持文件上传的相关JAR包,具体如下。
· commons-fileupload-1.3.2.jar
· commons-io-2.5.jar
当完成页面表单和文件上传解析器的配置后,在Controller中编写文件上传的方法即可实现文件上传。
@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封装了上传到程序中的文件
MultipartFile接口中的主要方法
![](https://img2023.cnblogs.com/blog/2003270/202304/2003270-20230425020126493-852313236.png)
16.1.2 应用案例——文件上传
1.jar包
![](https://img2023.cnblogs.com/blog/2003270/202304/2003270-20230425020126812-365754689.png)
2.web.xml
3.springmvc-config.xml
<!-- 配置文件上传解析器MultipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置请求编码格式-->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
4.fileUpload.jsp
<head>
<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="${pageContext.request.contextPath }/fileUpload" method="post"
enctype="multipart/form-data" onsubmit="return check()">
上传人:<input id="name" type="text" name="name"/><br/>
请选择文件:<input id="file" type="file" name="uploadfile" multiple="multiple"/><br/>
<input type="submit" value="上传">
</form>
</body>
5.success.jsp和error.jsp
6.用于文件上传的控制器类FileUploadController
@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重新命名上传的文件名称
String newFilename = name + "_" + UUID.randomUUID() + "_" + originalFilename;
try {
file.transferTo(new File(dirPath + newFilename));
} catch (IOException e) {
e.printStackTrace();
return "error";
}
}
// 跳转到成功页面
return "success";
}else {
return "error";
}
}
}
7.启动服务器
在发布路径中找到upload文件夹,文件夹内存放了新命名的文件
![](https://img2023.cnblogs.com/blog/2003270/202304/2003270-20230425020126596-1166827435.png)
需要注意的是,upload文件夹是在项目的发布路径中,而不是创建的项目所在目录。如果未更改项目发布路径,则要去工作空间的metadata目录中寻找项目发布目录(路径为:workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\chapter16\uplo ad);如果将项目的发布路径已更改到Tomcat中,则需要在Tomcat的webapps目录中寻找项目。
查找项目发布目录:
根据Tomcat的 日志
![](https://img2023.cnblogs.com/blog/2003270/202304/2003270-20230425020126561-152962851.png)
CATALINA_BASE:tomcat工作目录
CATALINA_HOME:tomcat安装目录
CATALINA_TMPDIR:tomcat临时目录
JRE_HOME:tomcat使用的JRE
CLASSPATH:tomcat使用的类路径
根据工作目录找到
![](https://img2023.cnblogs.com/blog/2003270/202304/2003270-20230425020126734-220549804.png)
打开xml文件
![](https://img2023.cnblogs.com/blog/2003270/202304/2003270-20230425020126655-1310629309.png)
这个路径就是发布路径。
问题:根据发布路径查找发现新文件不在指定目录下
原因:设置保存地址目录时设置错误,设置成了
String dirPath = request.getServletContext().getRealPath("/upload");
解决方法:
String dirPath = request.getServletContext().getRealPath("/upload/");
16.2 文件下载
16.2.1 实现文件下载
文件下载就是将文件服务器中的文件下载到本机上。
(1)在客户端页面使用一个文件下载的超链接,该链接的href属性要指定后台文件下载的方法以及文件名(需要先在文件下载目录中添加了一个名称为“1.jpg”的文件),具体代码示例如下。
<a href="${pageContext.request.contextPath }/download?filename=1.jpg">
文件下载
</a>
(2)在后台Controller类中,使用ResponseEntity可以很方便地定义返回的HttpHeaders对象和HttpStatus对象,通过对这两个对象的设置,即可完成下载文件时所需的配置信息。
@RequestMapping("/download")
public ResponseEntity<byte[]> fileDownload(HttpServletRequest request, String filename) throws Exception{
// 指定要下载的文件所在路径
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);
// 使用Sring MVC框架的ResponseEntity对象封装返回下载数据
return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK);
}
在fileDownload()方法中,首先根据文件路径和需要下载的文件名来创建文件对象,然后对响应头中文件下载时的打开方式以及下载方式进行了设置,最后返回ResponseEntity封装的下载结果对象。ResponseEntity对象有些类似前面章节介绍的@ResponseBody注解,它用于直接返回结果对象。上面示例中,设置响应头信息中的MediaType代表的是Interner Media Type(即互联网媒体类型),也叫作MIME类型,MediaType.APPLICATION_OCTET_STREAM的值为application/octet-stream,即表示以二进制流的形式下载数据;HttpStatus类型代表的是Http协议中的状态,示例中的HttpStatus.OK表示200,即服务器已成功处理了请求。
3.启动服务器
问题:java.io.FileNotFoundException: File 'G:\github\pro07-javaweb-begin\out\artifacts\pro07_javaweb_begin_war_exploded\upload\null' does not exist
![](https://img2023.cnblogs.com/blog/2003270/202304/2003270-20230425020126721-1477999545.png)
原因:路径错误
解决方法:超链接?后没有空格
<a href="${pageContext.request.contextPath }/download?filename=1.jpg">
16.2.2 中文名称的文件下载
可以使用Servlet API中提供的URLEncoder类中的encoder(String s, String enc)方法将中文转为UTF-8编码。该方法中第一个参数表示需要转码的字符串,第二个参数表示编码格式。
1.download.jsp
<a href="${pageContext.request.contextPath }/download?filename=<%=URLEncoder.encode("猫.jpg","UTF-8")%>">
文件下载(中文
</a>
2.controller
fileDownload方法
// 对文件名编码,防止中文文件乱码
filename = this.getFilename(request,filename);
新建getFilename方法
/*
根据浏览器的不同进行编码设置,返回编码后的文件名
*/
public String getFilename(HttpServletRequest request,String filename) throws UnsupportedEncodingException {
// IE不同版本User-Agent中出现的关键词
String[] IEBrowserKeyWords = {"MSIE","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");
}
IE为UTF-8(注意User-Agent中的关键字),其他浏览器设置为ISO-8859-1【但是现在已经没有ie浏览器了】
【思考题】
1.请简述上传表单需要满足的3个条件。
form?
2.请简述如何解决中文文件名称下载时的乱码问题。
用一个字符编码转化方法,IE用utf-8,其他用iso