
我们知道,目前Springboot技术在开发中用得越来越广泛了,而文件上传也是企业开发中很常见的一个功能,相信很多小伙伴都知道文件上传是怎么实现的。但是目前市面上,却很少有人从源码的角度进行解析,来告诉你为什么可以实现文件上传,已经底层对文件上传是怎么处理的。所以今天九哥就来和大家聊一聊,在Springboot环境下如何上传文件,以及文件上传的源码解析。
关注Java架构栈,干货天天都不断哦!
一. 实现文件上传
1.添加依赖包
首先我们准备一个Springboot的Web项目,这里我们只需要引入spring-boot-starter-web依赖即可,POM文件如下。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.9.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.qfsy</groupId>
<artifactId>fileupload</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>fileupload</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>2.编写Controller接口
接着再编写一个负责文件上传的Controller,代码如下:
@RestController
public class UploadController {
@PostMapping("/upload")
public String up(MultipartFile file) throws IOException {
// 获取当前工作目录,springboot的当前工作目录在idea中是项目根目录,打包后是当前jar包所在目录
final String dir = System.getProperty("user.dir");
// 上传文件的文件名
final String originalFilename = file.getOriginalFilename();
// 拼装最终文件保存位置
final String finalFilePath = dir + originalFilename;
// 核心方法,保存文件
file.transferTo(new File(finalFilePath));
return "ok";
}
}3.编写页面表单
还要准备一个文件上传的表单,在这里需要设置请求发送方式为post,请求的编码类型为multipart/form-data,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="file"><br>
<input type="submit" value="提交">
</form>
</body>
</html>4.properties配置文件
我们还需要编写一个application.properties配置文件,在该文件中上传主要的配置参数。
# 是否开启文件上传支持,默认值开启
spring.servlet.multipart.enabled=true
# 单个文件大小限制,默认值1MB
spring.servlet.multipart.max-file-size=1MB
# 文件上传请求体总大小(多文件上传),默认值10MB
spring.servlet.multipart.max-request-size=10MB5.目录文件结构
最后我们来看看该项目的目录结构,如下图所示:

利用上面的代码,我们很容易就可以实现文件上传,这些都不是问题。但是我们学习知识,需要知其然,更要知其所以然,所以接下来九哥就给大家分析一下文件上传的实现过程及其底层原理。
二. Http文件上传请求数据格式
当浏览器发起文件上传的请求,该请求的请求头和请求体,其实与普通请求是有一些区别的。文件上传的请求头中,有一个特殊的请求头Content-Type,该请求头的内容中有个boundary属性,声明了请求体中各部分内容使用的分隔符。

另外在请求体中的数据,会使用boundary分隔符号进行分隔。服务器端接收到数据后,根据分隔符即可对文件数据进行解析。

那么Java源码底层是如何对这些请求进行处理的呢?请大家继续跟我来了解一下吧。
三. 文件上传请求的源码分析
1. 文件上传请求的处理流程
SpringBoot内部处理Web请求的核心,其实依然是基于SpringMVC框架。也就是说,任何请求都会交给核心前端控制器DispatcherServlet进行处理,所以我们要想分析文件上传的请求,也要从DispatcherServlet入手。

从源码中我们可以看出,DispatcherServlet会检查当前请求是不是一个文件上传请求。如果是,则会调用解析器解析请求体中的数据,并将上面的flag标记设置为true,代表当前请求确实是一个文件上传请求。

接着DispatcherServlet会根据url,寻找处理当前文件上传请求的handler(即我们自己编写的controller方法)

然后DispatcherServlet查找可以执行该handler的适配器对象。

下一步就会调用拦截器,如果当前请求的url有匹配的拦截器,那么在这里会调用拦截器的前置方法。

接着调用Handler进行具体的文件上传处理。

然后调用拦截器的后置方法。

最后释放文件上传使用的临时文件资源。

2. 细节补充
2.1 临时文件的处理
在SpringBoot处理文件上传请求时,当请求调用到后端的handler时,文件已经在系统的临时目录中生成了。而当调用完transfer方法后,如果方法调用成功,会把临时文件自动清除掉。但是在执行这个方法之前如果发生异常,就需要靠DispathcerServlet中的fianlly代码块来完成临时文件的清理。

2.2 MultiPart解析器
SpringBoot解析文件上传请求,不用单独引入Apache-commons相关的依赖,因为SpringBoot内置了对应的解析器。


至此,九哥就带大家实现了SpringBoot中的文件上传功能,并带大家从源码的角度分析了文件上传的底层原理,现在你明白为什么SpringBoot可以实现文件上传了吧?

扫码或者点击文末左下角【阅读原文】快速进入学习!

点击【阅读原文】,免费开始学习Springboot视频课程!
原文作者:Java架构栈;如有侵权,请联系管理员删除。