准备工作
文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。
前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器
表单中的enctype属性详细说明:
-
application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式
-
multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码
-
text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件
文件上传
- 编写一个页面进行上传文件操作
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>upload</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/upload1" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit">
</form>
</body>
</html>
一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应
- Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成,而Spring MVC则提供了更简单的封装。
- Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
- Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件
- 导入文件上传的jar包(依赖),commons-fileupload
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>3.0-alpha-1</version>
</dependency>
<!--文件上传下载-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
- 配置multipartResolverbean交给Spring来管理
注意bean的id必须为multipartResolver,因为spring会调用这个bean的名字multipartResolver,否则会包上传文件400错误
<!--注册文件上传配置交给Spring管理-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上传文件大小上限,单位为字节(10485760=10M) -->
<property name="maxUploadSize" value="10485760"/>
<!-- 设置最大内存大小-->
<property name="maxInMemorySize" value="40960"/>
</bean>
- 编写Controller类进行接收上传的文件并储存写入目录文件中
CommonsMultipartFile的常用方法:
- String getOriginalFilename():获取上传文件的原名
- InputStream getInputStream():获取文件流
- void transferTo(File dest):将上传文件保存到一个目录文件中
方式一:上传文件原生的IO流处理
@PostMapping("/upload")
//@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile对象
public String upload(@RequestParam("file")CommonsMultipartFile file, HttpServletRequest request) throws IOException {
//获取文件名 : file.getOriginalFilename();
String uploadFileName = file.getOriginalFilename();
//如果上传文件名为""则重定向到上传文件页面
if ("".equals(uploadFileName)){
return "redirect:/index.jsp";
}
//设置上传文件保存在当前服务器的目录
//设置一个当前项目下的/upload目录下的字符串路径
String realPath = request.getRealPath("/upload");
//将该字符串路径变为一个文件File对象
File f = new File(realPath);
//如果文件路径目录不存在则创建
if (!f.exists()){
f.mkdir();
}
//创建文件输入流
InputStream inputStream = file.getInputStream();
//创建文件输出流对象,传入文件将/upload做为目录,文件名为该上传文件的文件名
FileOutputStream fileOutputStream = new FileOutputStream(new File(realPath,uploadFileName));
//创建一个byte数组用来接收上传文件的字节
byte[] bytes=new byte[1024];
//如果读取的文件字节不等于-1
while (inputStream.read(bytes)!=-1){
//则将读取的文件字节写出到目录下的文件对象
fileOutputStream.write(bytes);
//刷新文件输出流缓存
fileOutputStream.flush();
}
//关闭流
inputStream.close();
fileOutputStream.close();
//响应给浏览器
return "ok";
}
方式二:采用file.Transto 来保存上传的文件
@PostMapping("/upload1")
//@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile对象
public String upload1(@RequestParam("file")CommonsMultipartFile file,HttpServletRequest request) throws IOException {
//获取文件名(包括后缀名) : file.getOriginalFilename();
String uploadFileName = file.getOriginalFilename();
//设置一个当前项目下的/upload目录下的字符串路径
String realPath = request.getRealPath("/upload");
//如果上传文件名为""则重定向到上传文件页面
if ("".equals(uploadFileName)){
return "redirect:/index.jsp";
}
//将该字符串路径变为一个文件File对象
File f = new File(realPath);
//如果文件路径目录不存在则创建
if (!f.exists()){
f.mkdir();
}
//通过CommonsMultipartFile的transferTo方法直接写文件(将文件的全路径和文件名拼接)
file.transferTo(new File(realPath+"/"+uploadFileName));
//响应给浏览器
return "ok";
}
方式三:采用批量上传文件
- 前端上传页面需要在input标签内设置一个可以允许多个文件上传的属性multiple
<form action="${pageContext.request.contextPath}/upload2" method="post" enctype="multipart/form-data">
<input type="file" multiple="multiple" name="files">
<input type="submit">
</form>
- 编写Controller类,接收的文件类型的参数需要是CommonsMultipartFile[]数组类型
@PostMapping("/upload2")
//@RequestParam("files") 将name=files控件得到的批量文件封装成CommonsMultipartFile[]数组对象
public String upload2(@RequestParam("files")CommonsMultipartFile[] files,HttpServletRequest request) throws IOException {
//设置一个当前项目下的/upload目录下的字符串路径
String realPath = request.getRealPath("/upload");
//将该字符串路径变为一个文件File对象
File file = new File(realPath);
//如果文件路径目录不存在则创建
if (file.exists()){
file.mkdir();
}
//循环遍历出CommonsMultipartFile[]数组中的CommonsMultipartFile文件对象
for (int i = 0; i <files.length ; i++) {
CommonsMultipartFile multipartFile = files[i];
//获取文件名(包括后缀名) : file.getOriginalFilename();
String filename = multipartFile.getOriginalFilename();
//如果上传文件名为""则重定向到上传文件页面
if ("".equals(filename)){
return "redirect:/index.jsp";
}
//通过CommonsMultipartFile的transferTo方法直接写文件(将文件的全路径和文件名拼接)
multipartFile.transferTo(new File(realPath+"/"+filename));
}
//返回浏览器
return "ok";
}
文件下载
- 准备好需要下载的文件目录及文件
- 编写Controller类,设置响应头以及读取文件、输入输出流操作、关闭流
@RequestMapping("/download")
public String download(HttpServletRequest request, HttpServletResponse response) throws IOException {
//浏览器下载的图片地址,即服务器要放入资源给浏览器下载
//获取服务器端要下载的文件目录以及文件
//文件路径
String realPath = request.getRealPath("/upload");
//文件
String fileName="学习大纲.png";
//设置response 响应头
/*一般用于下载文件操作时:
*空白行的出现原因,jsp代码编译后产生。
* 就是有jsp生成html文件的时候,html文件内部会出现很多空白行。
* 下载后的文件内的空白行也是这样产生的, response.reset() 来清除首部的空白行
*/
response.reset();
//设置字符编码
response.setCharacterEncoding("utf-8");
//设置文件传输以二进制传输数据
response.setContentType("multipart/form-data");
//设置响应头
response.setHeader("Content-Disposition",
"attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));
//创建文件输入流(传入当前服务器端文件全路径对象)
FileInputStream fis=new FileInputStream(new File(realPath,fileName));
//获取输出流对象
OutputStream outputStream = response.getOutputStream();
//创建一个byte[]字节数组
byte[] bytes=new byte[1024];
int index=0;
//如果读取的字节不等于-1
while ((index = fis.read(bytes))!=-1){
//则写出到浏览器
outputStream.write(bytes,0,index);
//清空缓存
outputStream.flush();
}
//关闭流操作
fis.close();
outputStream.close();
//响应浏览器
return "ok";
}
- 启动Tomcat,测试下载结果
下载成功后