SpringBoot2 | 第二十篇:使用SpringBoot上传文件

​ 在工作中用的都是工具类,对 IO流 的使用也忘得差不多了,写个 Demo 来复习一下。spring boot 的文件上传与 spring mvc 的文件上传基本一致,只需注意一些配置即可。

[TOC]

环境/版本一览:

  • 开发工具:Intellij IDEA 2018.2.2
  • springboot: 2.0.6.RELEASE
  • jdk:1.8.0_171
  • maven:3.3.9
  • spring-boot-starter-thymeleaf: 2.0.6.RELEASE

1、pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<!-- 引入 Thymeleaf 模板引擎依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

2、application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
thymeleaf:
cache: false
servlet:
multipart:
# 设置单个文件最大值
max-file-size: 10MB
# 设置总上传数据最大值
max-request-size: 10MB

# 文件上传路径
upload:
path: E:/upload/fatal/
# path: /usr/local/upload/fatal/

3、templates

upload.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
</head>
<body>
<h1>Spring Boot file upload example</h1>

<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="file" name="file" multiple="multiple"/><br/><br/>
<input type="submit" value="Submit" />
</form>
</body>
</html>

uploadStatus.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>上传状态</title>
</head>
<body>

<h1>Spring Boot - Upload Status</h1>

<div th:if="${message}">
<h2 th:text="${message}"/>
</div>

<div th:if="${files}">
<h3 th:text="${files}"/>
</div>
</body>
</html>

4、FileUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package com.fatal.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;

/**
* 文件工具类
* @author: Fatal
* @date: 2019/7/21 0021 12:46
*/
@Slf4j
@Component
public class FileUtil {

@Value("${upload.path}")
private String uploadPath;

/**
* 上传一个或多个文件
*/
public void uploadFiles(MultipartFile[] files) {
Arrays.stream(files)
.forEach(this::uploadByFiles);
// .forEach(this::uploadByFileStream);
// .forEach(this::uploadByMultipartFile);
}

/**
* @param file 文件
* @param filePath 文件保存路径
* @param filename 文件名(包括后缀)
* private void uploadByxxx(MultipartFile file)
* 注意:这三个方法都一样,如果图片名字格式相同,则会覆盖
*/

/**
* FileStreams
*/
private void uploadByFileStream(MultipartFile file) {
FileInputStream in = null;
FileOutputStream out = null;
try {
long start = System.currentTimeMillis();
String filename = file.getOriginalFilename();
in = (FileInputStream)file.getInputStream();
File directory = new File(uploadPath);
initDirectory(directory);
out = new FileOutputStream(uploadPath + filename);
byte[] bytes = new byte[1024];
int len = 0;
while ((len = in.read(bytes)) != -1) {
out.write(bytes, 0, len);
}
long end = System.currentTimeMillis();
log.info("【文件 {} 上传耗时】 [{}]", filename, end - start);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (in!= null) {
try {
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}


/**
* nio
*/
private void uploadByFiles(MultipartFile file) {
try {
long start = System.currentTimeMillis();
String filename = file.getOriginalFilename();
byte[] bytes = new byte[1024];
Path path = Paths.get(uploadPath + filename);
File directory = new File(uploadPath);
initDirectory(directory);
Files.write(path, bytes);
long end = System.currentTimeMillis();
log.info("【文件 {} 上传耗时】 [{}]", filename, end - start);
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* SpringMVC 封装的上传文件的方法 MultipartFile.transferTo(File dest)
*/
private void uploadByMultipartFile(MultipartFile file) {
try {
long start = System.currentTimeMillis();
String filename = file.getOriginalFilename();
File directory = new File(uploadPath);
initDirectory(directory);
file.transferTo(new File(uploadPath + filename));
long end = System.currentTimeMillis();
log.info("【文件 {} 上传耗时】 [{}]", filename, end - start);
} catch (IOException e){
e.printStackTrace();
}
}

/**
* 初始化文件夹
* @param directory
*/
private void initDirectory(File directory) {
// 判断文件是否存在,不存在则新建一个
if (!directory.exists()) {
boolean mkdirs = directory.mkdirs();
if (!mkdirs) {
log.error("【文件上传】 文件夹创建失败 -[{}]", uploadPath);
}
}
}

}

5、handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.fatal.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

/**
* 全局异常处理器
* @author: Fatal
* @date: 2018/10/26 0026 11:19
*/
@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(RuntimeException.class)
public String handler(RuntimeException e, RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("message", e.getMessage());
return "redirect:/uploadStatus";
}

}

6、controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.fatal.controller;

import com.fatal.utils.FileUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
* 文件上传控制器
* @author: Fatal
* @date: 2018/10/25 0025 19:47
*/
@Controller
public class FileController {

@Autowired
private FileUtil fileUtil;

@GetMapping("/")
public String index() {
return "upload";
}

@GetMapping("/uploadStatus")
public String uploadStatus() {
return "uploadStatus";
}

@PostMapping("/upload")
public String fileUpload(@RequestParam("file") MultipartFile[] files,
RedirectAttributes redirectAttributes) {
if (files.length == 0) {
redirectAttributes.addFlashAttribute("message", "Select at least one file to upload");
return "redirect:uploadStatus";
}

try {
fileUtil.uploadFiles(files);
List<String> fileNames = Arrays.stream(files)
.map(MultipartFile::getOriginalFilename)
.collect(Collectors.toList());
redirectAttributes.addFlashAttribute("message", "Successful file uploaded");
redirectAttributes.addFlashAttribute("files", fileNames);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("uploaded fail");
}

return "redirect:/uploadStatus";
}

}

7、测试

启动项目

访问 http://localhost:8080

选择上传文件

1563704716326

点击submit

1563704776526

看看控制台

1563704880753

看看文件是否上传成功

1563704817867

参考链接

Spring Boot(十七):使用 Spring Boot 上传文件

总结

SpringBoot的知识已经有前辈在我们之前探索了。比较喜欢的博主有:唐亚峰 | Battcn方志朋的专栏程序猿DD纯洁的微笑。对这门技术感兴趣的可以去他们的博客逛逛。谢谢他们的分享~~

以上文章是我用来学习的Demo,都是基于 SpringBoot2.x 版本。

源码地址: https://github.com/ynfatal/springboot2-learning/tree/master/chapter20

学习 纯洁的微笑 前辈的经验