文件上传
一个简单的文件上传页面,点击添加文件,显示选择文件按钮,可上传多个文件
前端代码
upload.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<script th:src="@{/js/jquery-3.4.1.js}" type="text/javascript"></script>
</head>
<style>
form{
margin: 20px;
}
.form-group{
margin-top: 10px;
}
.btn-default{
display: none;
}
</style>
<body>
<div th:if="${uploadStatues}" th:text="${uploadStatues}">上传成功</div>
<!-- uploadStatues 为上传成功还是失败的标志,在后端写的 -->
<form th:action="@{/uploadFile}" method="post" enctype="multipart/form-data">
<!-- @{/uploadFile} 为表单处理数据的后端-->
上传文件
<input type="button" value="添加文件" id="add">
<div id="abc">
</div>
<button type="submit" class="btn btn-default">提交</button>
</form>
</body>
</html>
<script>
$("#add").click(function (){
var add = "<div class=\"form-group\">\n" +
" <input type=\"file\" name='upload'>\n" +
" </div>";
$("#abc").append(add);
$(".btn-default").css("display","block");
})
</script>
然后就是控制类了,首先要有一个访问页面的Mapping
@GetMapping("/upload")
public String Upload() {
return "upload";
}
因为表单提交后,要处理表单数据,action的值为url,所以还需要一个处理表单数据的Mapping ,因为表单method设置为post,所以需要一个PostMapping,接收post数据
@PostMapping("/uploadFile")
public String uploadFile(MultipartFile upload[], Model model) {
// upload[] 是 上传文件的name 通过name接收文件信息,为一个数组是因为传了多个文件
model.addAttribute("uploadStatues", "上传成功!");
for (MultipartFile file : upload) {
String fileName = file.getOriginalFilename(); // 获取原文件的名称
fileName = UUID.randomUUID() + "_" + fileName;
// 将原文件重新用UUID命名,避免重复,也可以用时间戳
String mkdir = "E:/spring_upload/"; // 上传的文件应该存放的路径
File filepath = new File(mkdir);
if (!filepath.exists()) {
filepath.mkdirs();
} // 如果路径不存在,建立该路径
try {
file.transferTo(new File(mkdir + fileName));
} catch (IOException e) {
e.printStackTrace();
model.addAttribute("uploadStatues", "上传失败!" + e.getMessage());
}
// 上传文件 抛出异常,如果失败返回失败代码
}
return "upload";
}
将上传成功与否的标志uploadStatues传回到原页面,所以 return “upload”,也可根据业务逻辑return至别的页面
测试结果
我们上传这三张图片
最后在E:/spring_upload下发现有这三张图片
最后虽然返回了upload,但访问路径仍为 uplodFile ,是 Spring Boot 中的转发方式,域发生改变但路径不变
重定向方式可以改变最后的访问路径
文件下载
我们只需要写一个代码告诉浏览器我现在要下载,然后弹出浏览器自带的下载框就行了
添加依赖
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
下载页面前端代码(down.html)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>下载文件</title>
</head>
<body>
<div>文件下载</div>
<a th:href="@{/download(filename='刘昊然jpg')}">下载</a>
</body>
</html>
就这么简单的前端,大家理解下载的精髓就好了,这个不重要
单击下载,是一个a标签 会跳转到一个链接,所以我们需要两个控制类
一个用于访问down.html页面
一个用于a标签的跳转并处理下载
访问down.html页面控制类:
@GetMapping("/todownload")
public String download() {
return "down";
}
a标签的跳转控制类:
@GetMapping("/download")
public ResponseEntity<byte[]> fileDownload(HttpServletRequest request,String filename) {
String mkdir = "E:/springboot/images";
File file = new File(mkdir+File.separator + filename);
/* File.separator 可代表任意系统下的文件分隔符,如果考虑跨平台,最好写成
File file = new File("E:" + File.separator + "springboot" + File.separator+"images"+filename);
*/
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment", filename);
// 通知浏览器以下载文件的方式打开 下载名称默认为 filename
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
// 以流的形式下载
try {
return new ResponseEntity<>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK);
// 返回文件
} catch (IOException e) {
e.printStackTrace();
return new ResponseEntity<byte[]>(e.getMessage().getBytes(), HttpStatus.EXPECTATION_FAILED);
// 失败 返回异常
}
}
如果下载文件的路径中有中文,要进行编码
在上面那个方法中加入
try {
filename= getFilename(request,filename);
} catch (Exception e) {
e.printStackTrace();
}
getFilename方法:
private String getFilename(HttpServletRequest request,String filename)
throws Exception {
String[] IEBrowserKeyWords = {"MSIE", "Trident", "Edge"};
String userAgent = request.getHeader("User-Agent");
for (String keyWord : IEBrowserKeyWords) {
if (userAgent.contains(keyWord)) {
return URLEncoder.encode(filename, "UTF-8").replace("+"," ");
}
}
return new String(filename.getBytes("UTF-8"), "ISO-8859-1");
}
File file = new File(mkdir+File.separator + filename);
File里面的参数是要下载文件的路径,filename 是a标签内传来的参数。
通常情况下载文件的文件名都是一个名称,而不是一个路径,所以我们将路径分成 路径+filename 的形式,便于 setContentDispositionFormData 的操作
将a标签内传参写成路径也没有问题,就是路径中的: / 在下载文件的默认名称中变成 _
像这样
运行结果: