需求:
写一个文件上传的功能,把文件上传到指定的文件夹。
然后上传成功后的文件回显
★ Spring Boot对文件上传提供的自动配置支持
Spring Boot的文件上传自动配置主要由 MultipartAutoConfiguration 和 MultipartProperties 两个类组成。
MultipartProperties负责加载 spring.servlet.multipart.* 开头的配置属性。
MultipartAutoConfiguration 则根据 MultipartProperties 读取的配置属性来初始化
StandardServletMultipartResolver 解析器对象。
MultipartAutoConfiguration:处理文件上传的类。
MultipartProperties :属性处理类,用来读取配置文件中的 spring.servlet.multipart.* 开头的配置属性
★ 文件上传的相关配置属性,由MultipartProperties类提供支持
# 设置每个文件上传域的最大大小
spring.servlet.multipart.max-file-size=10MB
# 设置整个请求支持的最大大小
spring.servlet.multipart.max-request-size=50MB
# 设置文件上传的中转目录
spring.servlet.multipart.location=d:/temp
★ 文件上传的步骤
(1)前端页面的文件请求以“multipart/form-data”编码方式提交请求。
(2)定义MultipartFile类型的属性用于封装文件上传域。
(3)通过MultipartFile属性即可访问被上传的文件,可以将该文件写入任意目录(或数据库)
▲ 希望看到文件上传回显
能看到上传后文件的图片,
(1)需要将服务器保存文件的所使用的文件名传回给视图页面。
(2)还需要将保存文件的路径设置为Spring Boot的静态资源路径。
添加额外的静态资源路径时,额外添加的磁盘最后必须要有一个斜杠结尾
文件上传:代码演示:
要上传文件的页面好看一些,需要这么一个文件上传控件。添加这个文件上传的界面组件。
问题:为什么这里选择了文件,但是却没有显示文件名
解决:
因为js文件是从上往下执行的。
效果:
选择文件后,成功显示文件名,为何能成功,就是通过 bsCustomFileInput.init(); ,让整个页面加载完之后,再初始化这个 custom-file-input 组件。让其能成功生效显示文件名。
方法参数介绍:
如果请求参数过多,不可能一个一个写,所以直接把请求参数封装成一个对象就行
完整代码
添加文件的页面:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
<!-- 引入css样式,用 link 元素 , stylesheet 样式单 , .gz表示是打包的,但是springboot会自动解包 -->
<!-- 引入 Bootstrap 的 Web Jar 中的 CSS 样式 -->
<link rel="stylesheet" th:href="@{'/webjars/bootstrap/css/bootstrap.min.css'}">
<!-- jquery 放在 bootstrap 前面,因为 bootstrap 需要依赖到 jquery -->
<!-- 引入 jQuery 的 Web Jar 中的 js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/jquery/jquery.min.js'}"></script>
<!-- 引入 Bootstrap 的 Web Jar 中的 js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/bootstrap/js/bootstrap.bundle.min.js'}"></script>
<!-- 引入 popper 的 Web Jar 中的 Js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/popper.js/umd/popper.min.js'}"></script>
<!-- 引入文件上传的界面组件的js -->
<script type="text/javascript"
th:src="@{'/webjars/bs-custom-file-input/dist/bs-custom-file-input.js'}"></script>
<script>
// <!-- 当整个页面加载完成的时候,就执行这个函数,保证这个函数是在页面加载完成后才执行初始化 -->
// <!-- 初始化 custom file input 组件的 JS 脚本 -->
$(function () {
bsCustomFileInput.init();
})
</script>
</head>
<body>
<div class="container">
<h4>添加图书</h4>
<!-- 保证请求是以 multipart/form-data 格式提交请求
不用这个格式,文件上传的数据都不会传到服务器 -->
<form method="post" th:action="@{/addBookFile}" enctype="multipart/form-data">
<div class="form-group row">
<label for="name" class="col-sm-2 col-form-label">图书名:</label>
<div class="col-sm-7">
<input type="text" id="name" name="name"
class="form-control" placeholder="请输入图书名">
</div>
<div class="col-sm-3 text-danger">
<span th:if="${book != null}" th:errors="${book.name}">错误提示</span>
</div>
</div>
<div class="form-group row">
<label for="cover" class="col-sm-2 col-form-label">图书封面:</label>
<div class="col-sm-7">
<!-- custom-file 用来保证页面比较好看 -->
<div class="custom-file">
<input type="file" id="cover" name="cover"
class="custom-file-input">
<label class="custom-file-label" for="cover">选择文件</label>
</div>
</div>
<div class="col-sm-3 text-danger">
<span th:if="${book != null}" th:errors="${book.cover}">错误提示</span>
</div>
</div>
<div class="form-group row">
<div class="col-sm-6 text-right">
<button type="submit" class="btn btn-primary">添加</button>
</div>
<div class="col-sm-6">
<button type="reset" class="btn btn-danger">重设</button>
</div>
</div>
</form>
</div>
</body>
</html>
配置文件:
设置上传文件的目录
创建一个Book对象用来封装前端传来的文件的数据。
后端业务逻辑代码:
package cn.ljh.file_upload.controller;
import cn.ljh.file_upload.domain.Book;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.UUID;
@Controller
public class FileUpLoadController {
//指定文件上传的存储路径,把路径写在配置文件中,通过 @Value 注解来获取该属性值
@Value("${ljh.myfile.savePath}")
private String destPath;
@SneakyThrows
@PostMapping("/addBookFile")
public String addBookFile(Book book, Model model){
//处理文件上传逻辑:也就是把文件内容保存到服务器的指定路径下。
//创建文件
File file = new File(destPath);
//判断文件是否存在
if (!file.exists()){
//如果没有该文件,也就是该目录不存在,则创建该目录
file.mkdir();
}
/*
* destFile 是服务器端重新生成的一个文件名,而且保留了原来的拓展名。
* 比如 我们上传文件的拓展名是 .jpg , 服务器生成的这个 destFile 文件,
* 不仅要有上传的文件的内容,拓展名(后缀名)也要保持一致
* Extension: 扩展 Original:起初的、原先的
*/
//获取上传文件的原拓展名
//获取上传文件的文件名
String originalFilename = book.getCover().getOriginalFilename();
//通过String工具类来获取这个文件名的扩展名
String ext = StringUtils.getFilenameExtension(originalFilename);
/*
* destPath + "/" : 指定文件上传的路径 ,路径和文件名之间需要加一个斜杠 /
* UUID.randomUUID().toString() : 生成文件名
* "." + ext : 文件的扩展名(后缀名)
*/
//生成一个新的文件
File destFile = new File(destPath + "/" + UUID.randomUUID().toString() + "." + ext);
//获取要上传文件的内容,返回是MultipartFile对象
MultipartFile cover = book.getCover();
//MultipartFile对象有一个方法 transferTo :可以将上传文件的内容写入到指定的文件destFile中。
cover.transferTo(destFile);
model.addAttribute("tip" , originalFilename + "文件上传成功!");
return "uploadfile_success";
}
}
简单的上传文件后的返回页面:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>上传成功</title>
<!-- 引入css样式,用 link 元素 , stylesheet 样式单 , .gz表示是打包的,但是springboot会自动解包 -->
<!-- 引入 Bootstrap 的 Web Jar 中的 CSS 样式 -->
<link rel="stylesheet" th:href="@{'/webjars/bootstrap/css/bootstrap.min.css'}">
<!-- jquery 放在 bootstrap 前面,因为 bootstrap 需要依赖到 jquery -->
<!-- 引入 jQuery 的 Web Jar 中的 js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/jquery/jquery.min.js'}"></script>
<!-- 引入 Bootstrap 的 Web Jar 中的 js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/bootstrap/js/bootstrap.bundle.min.js'}"></script>
<!-- 引入 popper 的 Web Jar 中的 Js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/popper.js/umd/popper.min.js'}"></script>
</head>
<body>
<div class="container">
<h4>文件上传成功</h4>
<div class="text-primary" th:text="${tip}">提示</div>
</div>
</body>
</html>
效果:
文件回显
▲ 希望看到文件上传回显
能看到上传后文件的图片,
(1)需要将服务器保存文件的所使用的文件名传回给视图页面。
(2)还需要将保存文件的路径设置为Spring Boot的静态资源路径。
添加额外的静态资源路径时,额外添加的磁盘最后必须要有一个斜杠结尾
代码演示:
没能成功回显图片,待后续研究。
封装参数的对象添加个 fileName 属性,用来返回上传之后的文件面
把文件名单独拎出来,设置到book对象里面
弄一个配置类,用于添加额外的静态资源路径,把图片的磁盘路径映射到静态资源路径里面,让项目可以读取到从而进行回显。
文件上传后的页面,回显的代码只有这么一句。
配置的磁盘路径,需要在最后加一条斜杠
结果:
没弄出来。
全部代码:
最终加上回显的代码(没回显成功,待后续研究)
index
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
<!-- 引入css样式,用 link 元素 , stylesheet 样式单 , .gz表示是打包的,但是springboot会自动解包 -->
<!-- 引入 Bootstrap 的 Web Jar 中的 CSS 样式 -->
<link rel="stylesheet" th:href="@{'/webjars/bootstrap/css/bootstrap.min.css'}">
<!-- jquery 放在 bootstrap 前面,因为 bootstrap 需要依赖到 jquery -->
<!-- 引入 jQuery 的 Web Jar 中的 js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/jquery/jquery.min.js'}"></script>
<!-- 引入 Bootstrap 的 Web Jar 中的 js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/bootstrap/js/bootstrap.bundle.min.js'}"></script>
<!-- 引入 popper 的 Web Jar 中的 Js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/popper.js/umd/popper.min.js'}"></script>
<!-- 引入文件上传的界面组件的js -->
<script type="text/javascript"
th:src="@{'/webjars/bs-custom-file-input/dist/bs-custom-file-input.js'}"></script>
<script>
// <!-- 当整个页面加载完成的时候,就执行这个函数,保证这个函数是在页面加载完成后才执行初始化 -->
// <!-- 初始化 custom file input 组件的 JS 脚本 -->
$(function () {
bsCustomFileInput.init();
})
</script>
</head>
<body>
<div class="container">
<h4>添加图书</h4>
<!-- 保证请求是以 multipart/form-data 格式提交请求
不用这个格式,文件上传的数据都不会传到服务器 -->
<form method="post" th:action="@{/addBookFile}" enctype="multipart/form-data">
<div class="form-group row">
<label for="name" class="col-sm-2 col-form-label">图书名:</label>
<div class="col-sm-7">
<input type="text" id="name" name="name"
class="form-control" placeholder="请输入图书名">
</div>
<div class="col-sm-3 text-danger">
<span th:if="${book != null}" th:errors="${book.name}">错误提示</span>
</div>
</div>
<div class="form-group row">
<label for="cover" class="col-sm-2 col-form-label">图书封面:</label>
<div class="col-sm-7">
<!-- custom-file 用来保证页面比较好看 -->
<div class="custom-file">
<input type="file" id="cover" name="cover"
class="custom-file-input">
<label class="custom-file-label" for="cover">选择文件</label>
</div>
</div>
<div class="col-sm-3 text-danger">
<span th:if="${book != null}" th:errors="${book.cover}">错误提示</span>
</div>
</div>
<div class="form-group row">
<div class="col-sm-6 text-right">
<button type="submit" class="btn btn-primary">添加</button>
</div>
<div class="col-sm-6">
<button type="reset" class="btn btn-danger">重设</button>
</div>
</div>
</form>
</div>
</body>
</html>
uploadfile_success.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>上传成功</title>
<!-- 引入css样式,用 link 元素 , stylesheet 样式单 , .gz表示是打包的,但是springboot会自动解包 -->
<!-- 引入 Bootstrap 的 Web Jar 中的 CSS 样式 -->
<link rel="stylesheet" th:href="@{'/webjars/bootstrap/css/bootstrap.min.css'}">
<!-- jquery 放在 bootstrap 前面,因为 bootstrap 需要依赖到 jquery -->
<!-- 引入 jQuery 的 Web Jar 中的 js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/jquery/jquery.min.js'}"></script>
<!-- 引入 Bootstrap 的 Web Jar 中的 js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/bootstrap/js/bootstrap.bundle.min.js'}"></script>
<!-- 引入 popper 的 Web Jar 中的 Js 脚本 -->
<script type="text/javascript" th:src="@{'/webjars/popper.js/umd/popper.min.js'}"></script>
</head>
<body>
<div class="container">
<h4>文件上传成功</h4>
<div class="text-primary" th:text="${tip}">提示</div>
<!-- 文件上传后进行回显 -->
<img th:src="@{'/uploads/' + ${book.fileName}}">
</div>
</body>
</html>
Book
package cn.ljh.file_upload.domain;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
@Data
public class Book {
private Integer id;
private String name;
//用来封装前端传来的文件数据
private MultipartFile cover;
//用于封装上传之后的文件名
private String fileName;
}
FileUpLoadController
package cn.ljh.file_upload.controller;
import cn.ljh.file_upload.domain.Book;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.UUID;
@Controller
public class FileUpLoadController {
//指定文件上传的存储路径,把路径写在配置文件中,通过 @Value 注解来获取该属性值
@Value("${ljh.myfile.savePath}")
private String destPath;
@SneakyThrows
@PostMapping("/addBookFile")
public String addBookFile(Book book, Model model){
//处理文件上传逻辑:也就是把文件内容保存到服务器的指定路径下。
//创建文件
File file = new File(destPath);
//判断文件是否存在
if (!file.exists()){
//如果没有该文件,也就是该目录不存在,则创建该目录
file.mkdir();
}
/*
* destFile 是服务器端重新生成的一个文件名,而且保留了原来的拓展名。
* 比如 我们上传文件的拓展名是 .jpg , 服务器生成的这个 destFile 文件,
* 不仅要有上传的文件的内容,拓展名(后缀名)也要保持一致
* Extension: 扩展 Original:起初的、原先的
*/
//获取上传文件的原拓展名
//获取上传文件的文件名
String originalFilename = book.getCover().getOriginalFilename();
//通过String工具类来获取这个文件名的扩展名
String ext = StringUtils.getFilenameExtension(originalFilename);
/*
* destPath + "/" : 指定文件上传的路径 ,路径和文件名之间需要加一个斜杠 /
* UUID.randomUUID().toString() : 生成文件名
* "." + ext : 文件的扩展名(后缀名)
*/
//生成一个新的文件
//生成文件名
String fileName = UUID.randomUUID().toString() + "." + ext;
File destFile = new File(destPath + "/" + fileName);
//获取要上传文件的内容,返回是MultipartFile对象
MultipartFile cover = book.getCover();
//MultipartFile对象有一个方法 transferTo :可以将上传文件的内容写入到指定的文件destFile中。
cover.transferTo(destFile);
//文件上传成功后回显的名字
book.setFileName(fileName);
model.addAttribute("tip" , originalFilename + "文件上传成功!");
return "uploadfile_success";
}
}
FileUpLoadConfig
package cn.ljh.file_upload.config;
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 FileUpLoadConfig implements WebMvcConfigurer {
//指定文件上传的存储路径,把路径写在配置文件中,通过 @Value 注解来获取该属性值
@Value("${ljh.myfile.savePath}")
private String destPath;
//重写这个方法,用于添加额外的静态资源路径
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//将文件上传的destPath这个磁盘路径添加成静态资源路径,并映射到 /uploads/** 路径下。
registry.addResourceHandler("/uploads/**")
.addResourceLocations(destPath);
}
}
application.yml
#指定文件上传后保存的路径
ljh:
myfile:
savePath: F:\myCode\crazyCode\SpringBoot\springboot07\uploads\