本章主要记录Spring MVC实现简单的文件上传和下载。涉及到的知识点有:
- CommonsMultipartResolver
:通用多部件解析器,用来实现多文件上传
- MultipartFile
:多部件文件,用来传输多种类型的文件
- 两种文件保存方式:1.通过BufferedOutputStream
写入服务器。2.通过MultipartFile封装的方法写入。
- 两种文件下载方式:通过设置Content-Disposition
为inline
和attachment
试下浏览器显示和文件下载。
1.文件上传
1.1.spring-mvc-servlet.xml
添加CommonsMultipartResolver
,用于解析POST请求中的MultiPartFile
.
<!--文件上传-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--一次上传限制10Mb(一次可以上传多种多个文件)-->
<property name="maxUploadSize" value="10485760"/>
<property name="defaultEncoding" value="utf-8"/>
</bean>
1.2.FileUtils.java
两种上传方式
1. saveFileByStream通过原生的BufferedOutputStream将将文件保存在服务器
2. saveFileByMultiPartFile通过MultiPartFile.transferTo()将文件保存
package pers.hanchao.hespringmvc.file;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
/**
* <p>简单的文件上传和下载实例,
* 文件上传实现了两种方式的上传:流和MultiPartFile;
* 文件下载实现了两种方式的下载:inline和attachment</p>
* @author hanchao 2018/1/24 22:35
**/
@Component
@RequestMapping("file")
public class FileUtils {
private static final Logger LOGGER = Logger.getLogger(FileUtils.class);
/**
* <p>上传文件的两种方式:流上传和Multipart上传</p>
* @author hanchao 2018/1/22 22:22
**/
@PostMapping("/upload")
public String upload(HttpServletRequest request, @RequestParam("file") MultipartFile[] files,Model model){
boolean isSuccess = true;
//获取上传根目录
String uploadRootPath = request.getServletContext().getRealPath("upload");
LOGGER.debug("uploadRoot:" + uploadRootPath);
File uploadRootDir = new File(uploadRootPath);
//判断上传根目录是否存在,如果不存在,则创建层级目录
if (!uploadRootDir.exists()){
uploadRootDir.mkdirs();
}
//上传文件
List<File> savedFileList = new ArrayList<File>();
for (int i = 0; i < files.length; i++) {
MultipartFile file = files[i];
//如果文件大于200kb,则不能上传
if (file.getSize() > 204800){
isSuccess = false;
break;
}
/*
两种上传方式
1.saveFileByStream通过原生的BufferedOutputStream将将文件保存在服务器
2.saveFileByMultiPartFile通过MultiPartFile.transferTo()将文件保存
*/
File savedFile = saveFileByStream(file, uploadRootDir);
// File savedFile = saveFileByMultiPartFile(file, uploadRootDir);
savedFileList.add(savedFile);
}
if (!isSuccess){
model.addAttribute("returnMessage","文件太大,禁止上传!");
LOGGER.error("文件太大,禁止上传!");
}else {
model.addAttribute("savedFileList",savedFileList);
}
return "/file/file";
}
/**
* <p>通过流操作保存文件</p>
* @param srcFile 需要保存的文件
* @param uploadRootDir 上传文件根目录
* @return 上传完的文件
* @author hanchao 2018/1/22 22:09
**/
private File saveFileByStream(MultipartFile srcFile, File uploadRootDir) {
//获取客户端名称
String srcFileName = srcFile.getOriginalFilename();
LOGGER.debug("客户端文件名称:" + srcFileName);
File serverFile = null;
if (srcFileName != null && srcFileName.length() > 0){
try{
byte[] bytes = srcFile.getBytes();
//创建服务器文件
serverFile = new File(uploadRootDir.getAbsolutePath() + File.separator + srcFileName);
//写入服务器文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(serverFile));
bos.write(bytes);
bos.close();
//打印上传了哪些文件
LOGGER.info("写入了文件:" + serverFile);
} catch (IOException e) {
e.printStackTrace();
LOGGER.error("删除文件失败!");
}
}
return serverFile;
}
/**
* <p>通过MultipartFile的封装方法保存文件</p>
* @param srcFile 需要保存的文件
* @param uploadRootDir 上传文件根目录
* @return 上传完的文件
* @author hanchao 2018/1/23 22:09
**/
public File saveFileByMultiPartFile(MultipartFile srcFile, File uploadRootDir){
File serverFile = null;
//如果文件不为空,则可以上传
if (!srcFile.isEmpty()){
try{
serverFile = new File(uploadRootDir.getAbsolutePath() + File.separator + srcFile.getOriginalFilename());
srcFile.transferTo(serverFile);
//打印上传了哪些文件
LOGGER.info("写入了文件:" + serverFile);
} catch (IOException e) {
e.printStackTrace();
LOGGER.error("删除文件失败!");
}
}
return serverFile;
}
}
1.3.file.jsp
注意:
1. 一定要注意设置enctype="multipart/form-data"
2. 一定要注意name="file"
要与后台传参对应,如@RequestParam("file") MultipartFile[] files
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
Created by IntelliJ IDEA.
User: hanchao
Date: 2018/1/22
Time: 21:16
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<html>
<head>
<title>文件上传下载</title>
<style type="text/css">
div {margin: 5px 5px;text-align:center; border:1px solid #96c2f1; background:#eff7ff;}
</style>
</head>
<body>
<div style="width: 600px;">
<h3>文件上传和下载实例</h3>
</div>
<div style="width: 290px;height: 180px;float: left;">
<%--一定要注意设置enctype="multipart/form-data"--%>
<form method="post" action="/file/upload" enctype="multipart/form-data">
<label>单个文件上传</label><hr/>
<input type="file" name="file"/><hr/>
<input type="submit" value="上传"/>
</form>
</div>
<div style="width: 290px;height: 180px;float: left">
<%--一定要注意设置enctype="multipart/form-data"--%>
<form method="post" action="/file/upload" enctype="multipart/form-data">
<label>多个文件上传</label><hr/>
<%--一定要注意name="file"要与后台传参对应--%>
<input type="file" name="file"/><br/>
<input type="file" name="file"/><br/>
<input type="file" name="file"/><br/>
<input type="file" name="file"/><br/><hr/>
<input type="submit" value="上传"/>
</form>
</div>
</body>
</html>
1.4.result
上传结果:
2.文件下载
2.1.spring-mvc-servlet.xml
文件下载并没有依赖CommonsMultipartResolver
,无需配置。
2.2.FileUtils.java
设置文件下载方式:
1. inline:浏览器内打开此文件(如txt、xml等),不能打开的再下载(如rar等)
2. attachment:下载文件到本地
/**
* <p>下载</p>
* @param fileName 文件名
* @param suffix 文件后缀
* @param type 下载方式:inline[浏览器内显示]、attachment[下载]
* @author hanchao 2018/1/24 22:08
**/
@GetMapping("/download/{name}/{suffix}/{type}")
public void download(HttpServletRequest request,HttpServletResponse response,
@PathVariable("name") String fileName,@PathVariable("suffix") String suffix,
@PathVariable("type") String type, Model model){
//获取根目录路径
File uploadRootDir = new File(request.getServletContext().getRealPath("upload"));
//获取目标文件
File file = new File(uploadRootDir.getAbsolutePath() + File.separator + fileName + "." + suffix);
if (!file.exists()){
model.addAttribute("returnMessage","文件不存在!");
}else{
//获取文件类型
String mimeType = URLConnection.guessContentTypeFromName(file.getName());
//如果文件类型为空,则使用默认文件类型
if (null == mimeType){
mimeType = "application/octet-stream";
}
LOGGER.debug("mimeType:" + mimeType);
//设置contentType
response.setContentType(mimeType);
/*
设置文件下载方式:
1.inline:浏览器内打开此文件(如txt、xml等),不能打开的再下载(如rar等)
2.attachment:下载文件到本地
*/
response.setHeader("Content-Disposition",type + ";filename=\"" + file.getName() + "\"");
//设置长度
response.setContentLength((int) file.length());
//下载文件
InputStream inputStream = null;
try {
inputStream = new BufferedInputStream(new FileInputStream(file));
FileCopyUtils.copy(inputStream,response.getOutputStream());
inputStream.close();
response.getOutputStream().close();
} catch (FileNotFoundException e) {
e.printStackTrace();
LOGGER.error("下载文件失败");
} catch (IOException e) {
e.printStackTrace();
LOGGER.error("下载文件失败");
} finally {
try {
inputStream.close();
response.getOutputStream().close();
} catch (IOException e) {
e.printStackTrace();
LOGGER.error("下载文件失败");
}
}
}
}
2.3.file.jsp
<div style="width: 600px;">
<a href="/file/download/5/jpg/inline">显示5.jpg</a><hr/>
<a href="/file/download/11/xml/inline">显示11.xml</a><hr/>
<a href="/file/download/1/txt/inline">显示1.txt</a><hr/>
<a href="/file/download/5/jpg/attachment">下载5.jpg</a><hr/>
<a href="/file/download/11/xml/attachment">下载11.xml</a><hr/>
<a href="/file/download/1/txt/attachment">下载1.txt</a>
</div>