前言
最近我在B站的学相思学习了飞哥的spring-boot实现的文件上传功能,我又在原有的基础上加了一点新的功能,不再是单文件上传,而是多文件上传功能,视频中飞哥也提供了思路,闲着没事也可以看看他的视频,懂得学习别人的知识同时多做分享,时刻怀揣感恩的心。
https://www.bilibili.com/video/BV1C3411b7wt?spm_id_from=333.999.0.0
本地文件上传的原理
本地文件上传的原理,类似于文件的拷贝,就是从一个文件夹中拷贝到另一个文件夹中。
代码块
- 引入pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
- 创建测试静态HTML页面
upload.html
<body>
<h3>文件上传</h3>
<form action="/upload/file" enctype="multipart/form-data" method="post" id="image">
<input name="dir" value="bus">
<input type="file" name="file" multiple onchange="upload()">
</form>
<script>
function upload (){
document.getElementById("image").submit();
}
</script>
</body>
- 后端映射页面
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/page")
public class PageController {
@RequestMapping("/upload")
public String upload(){
return "upload";
}
}
-
全局配置
application.yaml
spring:
profiles:
# 激活测试环境
active: dev
freemarker:
suffix: .html
cache: false
servlet:
multipart:
# 是否支持多部分上传。
enabled: true
# 最大文件大小
max-file-size: 20MB
# 请求文件总大小
max-request-size: 100MB
# 文件写入磁盘的阈值。
file-size-threshold: 20MB
# 临时文件存储目录
location: G:/date/temp
server:
port: 8080
application-dev.yaml
file:
# 服务器对外静态资源访问目录
server-path: /uploads/**
# 本地服务静态资源储存目录
local-path: G:/temp/
# 服务器访问路径
static-path: http://localhost:8080
- 创建文件上传服务接口
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public interface UploadService {
Map<String, List<String>> upload(List<MultipartFile> multipartFile, String dir) throws IOException;
}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
@Service
public class UploadServiceImpl implements UploadService {
// 访问请求头
@Value("${file.static-path}")
public String staticPath;
// 本地储存地址
@Value("${file.local-path}")
public String localPath;
/**
* MultipartFile 这个对象是 SpringMVC提供的文件上传的可接受的类
* 他的底层自动会和HttpServletRequest request中的request.getInputStream()融合
* 从而达到文件上传的效果
*
* @author chenlirun
* @date 2021/11/20 10:14
*/
@Override
public Map<String, List<String>> upload(List<MultipartFile> multipartFiles, String dir) {
Map<String, List<String>> map = new HashMap<>();
List<String> strings = new ArrayList<>();
try {
for (MultipartFile multipartFile : multipartFiles) {
// 获取本地上传文件的文件真实名称
String realFileName = multipartFile.getOriginalFilename();
// 截取文件名的后缀
String imgSuffix = realFileName.substring(realFileName.lastIndexOf("."));
// 生成唯一的文件名称
String newFileName = UUID.randomUUID() + imgSuffix;
// 日期目录,做隔离,防止图片都放到一起,未来找的时候不好找
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String datePath = dateFormat.format(new Date());
File targetFile = new File(localPath + dir, datePath);
if (!targetFile.exists()) targetFile.mkdirs();
// 指定文件上传以后的文件目录
File fileName = new File(targetFile, newFileName);
// 指定文件上传的路径
multipartFile.transferTo(fileName);
// 返回访问路径地址
String accessName = staticPath + "/uploads" + "/" + dir + "/" + datePath + "/" + newFileName;
strings.add(accessName);
}
map.put("image",strings);
return map;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
- 创建后端控制层
import com.example.demo.service.UploadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@Controller
public class UploadController {
@Autowired
UploadService uploadService;
/**
* @Param multipartFile 上传的文件
* @Param HttpServletRequest request 获取请求头中 用户设定的路径信息
* @author chenlirun
* @date 2021/11/21 11:59
*/
@ResponseBody
@PostMapping("/upload/file")
public Object upload(@RequestParam("file") List<MultipartFile> multipartFile, HttpServletRequest request) throws IOException {
if (multipartFile.isEmpty())
return "文件上传有误";
String dir = request.getParameter("dir");
Map<String, List<String>> upload = uploadService.upload(multipartFile, dir);
return upload;
}
}
- 测试文件上传
上传文件功能触发后,我在后端打了一个dugger断点,可以看到我们上传文件时的信息。通过这些信息我们可以做很多事情
- 配置临时文件的地址
- 配置文件的大小
- 配置文件的格式
我们看到文件上传不会直接上传到真实文件目录中,他一定会先创建一个临时文件,经过临时文件的中转后,才到真实目录。作用是:
- 防止网络断开或者用户直接刷新或者是取消,因为这样会造成大量的垃圾文件的产生。
- 保证真实目录的文件真实有效。
完成上述的所有步骤,可以完成多文件上传的功能,并返回给我们一个json格式的字符串。
{"image":["http://localhost:8080/uploads/bus/2021-11-21/a1a65455-312b-42bb-b899-764e02f8bfd4.jpg"]}
但是我们并不能通过返回的json字符串路径访问到文件。但不要急我们下面会讲到。
- 配置静态资源访问目录
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Value("${file.server-path}")
public String serverPath;
@Value("${file.local-path}")
public String localPath;
/**
* springboot中SpringMVC让开发者去配置文件上传的额外静态资源服务配置
* @author chenlirun
* @date 2021/11/20 15:34
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
/**
* addResourceHandler() :暴露对外访问静态页面的地址
* addResourceLocations() : 本地存储静态资源的目录
*/
registry.addResourceHandler(serverPath).addResourceLocations("file:"+localPath);
}
}
完成这个配置就可以通过之前获得的地址访问了
http://localhost:8080/uploads/bus/2021-11-21/a1a65455-312b-42bb-b899-764e02f8bfd4.jpg