1. 补:Spring Boot 当中实现文件上传功能(附+源代码)
文章目录
- [1. 补:Spring Boot 当中实现文件上传功能(附+源代码)](#1. 补:Spring Boot 当中实现文件上传功能(附+源代码))
- [2. 准备工作](#2. 准备工作)
- [3. 开始演示:实现文件上传功能(附+源码)](#3. 开始演示:实现文件上传功能(附+源码))
-
- [3.1 第一种方式:静态创建方式](#3.1 第一种方式:静态创建方式)
- [3.2 第二种方式:动态创建方式](#3.2 第二种方式:动态创建方式)
- [4. 上传文件的注意事项](#4. 上传文件的注意事项)
- [5. 最后:](#5. 最后:)
2. 准备工作
我们在 pom.xml 文件当中导入相关的 jar 依赖。如下:
xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rainbowsea</groupId>
<artifactId>springboot-fileUpload</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 导入SpringBoot 父工程-规定写法-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
</parent>
<!-- 导入web项目场景启动器:会自动导入和web开发相关的jar包所有依赖【库/jar】-->
<!-- 后面还会在说明spring-boot-starter-web 到底引入哪些相关依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 引入 thymeleaf-start ,项目会自动完成配置,-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 引入 lombok 插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
编写项目的场景启动器:
java
package com.rainbowsea.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // 项目启动场景
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
编写相关前端 HTML页面,上传文件的页面。
注意:这里我们是前端使用的是
thymeleaf
视图解析器。所以关于前端映射文件的存放位置,不可以随便放。
htmlenctype="multipart/form-data" 文件提交 multiple 属性可以选中多个文件信息上传 <input type="reset" value="重新填写"><br> ; type="reset" 清空表单,重新填写表单
前端页面显示效果:
3. 开始演示:实现文件上传功能(附+源码)
3.1 第一种方式:静态创建方式
静态创建方式: 静态方式,先手动在指定的 windows 当中创建好目录,再放入上传的文件。到其目录当中。
java
package com.rainbowsea.springboot.controller;
import com.rainbowsea.springboot.utils.WebUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
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.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@Slf4j
@Controller // 控制器
public class UploadController {
// 处理转发到用户注册~可以完成文件上传页面
@GetMapping("/upload.html")
public String uploadPage() {
return "upload"; // 视图解析,pom_>jar,转发到templates/upload.html
// 注意如果配置了拦截器,资源的上传 upload.html页面也是要放行的
}
// 处理用户的注册请求-包括处理文件上传
// 静态方式,先手动在指定的windows当中创建目录,在放入文件
@PostMapping("/upload")
@ResponseBody
public String upload(
// 自动封装
String name,
String email,
Integer age,
String job,
@RequestParam("header") MultipartFile header,
@RequestParam("photos") MultipartFile[] photos
) throws IOException {
log.info("上传的信息 name={},email={} age = {} job={} ",name,email,age,job);
log.info("header={}",header);
log.info("photos={}",photos);
// 如果信息都成功得到,我们就将文件保存到指定的目录比如:E:\temp_upload
// 1. 我们需要先将文件保存到指定的目录,比如:E:\temp_upload
// 2. 后面我们在演示把文件保存到动态创建的目录当中
// 首先处理头像的文件,因为只有一张图片
if(!header.isEmpty()) { // 处理头像
String originalFilename = header.getOriginalFilename();
System.out.println(originalFilename);
header.transferTo(new File("E:\\temp_upload\\" +originalFilename) );
}
// 处理宠物的图片
if(photos.length > 0) {
for (MultipartFile photo :photos) {
String originalFilename = photo.getOriginalFilename();
System.out.println(originalFilename);
photo.transferTo(new File("E:\\temp_upload\\" + originalFilename));
}
}
return "注册用户成功/文件上传成功";
}
}
运行测试:http://localhost:8080/upload.html
3.2 第二种方式:动态创建方式
动态创建方式: 以项目作为根路径创建目录,存放文件。这种方式兼容性更好,没有盘符之说。可移植性更好。
动态创建的核心,就是获取到项目的根路径,从而以项目的根路径,作为参照/相对路径,从而创建存放上传文件的目录。
java
package com.rainbowsea.springboot.controller;
import com.rainbowsea.springboot.utils.WebUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
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.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@Slf4j
@Controller // 控制器
public class UploadController {
// 处理转发到用户注册~可以完成文件上传页面
@GetMapping("/upload.html")
public String uploadPage() {
return "upload"; // 视图解析,pom_>jar,转发到templates/upload.html
// 注意如果配置了拦截器,资源的上传 upload.html页面也是要放行的
}
// 处理用户的注册请求-包括处理文件上传
// 动态创建方式:以项目作为根路径创建目录,存放文件
// springboot-fileUpload/src/main/resources/static/images
@PostMapping("/upload")
@ResponseBody
public String upload(
// 自动封装
String name,
String email,
Integer age,
String job,
@RequestParam("header") MultipartFile header,
@RequestParam("photos") MultipartFile[] photos
) throws IOException {
log.info("上传的信息 name={},email={} age = {} job={} ", name, email, age, job);
log.info("header={}", header);
log.info("photos={}", photos);
String path = ResourceUtils.getURL("classpath:").getPath();
System.out.println("path:" + path); // 获取到的是这个项目的根路径(类路径)全的。
// 定义好,我们要在项目中的什么位置存放传过来的文件
//File file = new File(path + "static/images");
File file = new File(WebUtils.getUploadFileDirectory());
// 动态创建指定目录
if (!file.exists()) { // 如果目录不存在,我们就创建
file.mkdirs();
}
// 首先处理头像的文件,因为只有一张图片
if (!header.isEmpty()) { // 处理头像
String originalFilename = header.getOriginalFilename(); // 获取的文件的名字
System.out.println(file.getAbsolutePath()); // 获取到文件的绝对路径
String fileName = UUID.randomUUID().toString() + "_"+ System.currentTimeMillis()+"_"+originalFilename;
System.out.println(fileName);
header.transferTo(new File(file.getAbsolutePath() + "/" + fileName));
}
// 处理宠物的图片
if (photos.length > 0) {
for (MultipartFile photo : photos) {
String originalFilename = photo.getOriginalFilename();
System.out.println(file.getAbsolutePath()); // 获取到文件的绝对路径
String fileName = UUID.randomUUID().toString() + "_"+ System.currentTimeMillis()+"_"+originalFilename;
// 保存到动态创建的目录
photo.transferTo(new File(file.getAbsolutePath() + "/" + fileName ));
}
}
return "注册用户成功/文件上传成功";
}
}
4. 上传文件的注意事项
- Spring Boot 的文件上传: 注意如果配置了拦截器,资源的上传 upload.html页面也是要放行的,不然会上传失败。
- 注意:在Spring Boot 当中:默认单个文件最大1MB,一次上传多个文件最大10MB,我们可以修改
MultipartProperties类当中的属性,我们可以通过在 resources类路径下,application.yaml 进行修改
yaml
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 50MB
- 解决文件覆盖问题,如果文件名相同,会出现覆盖问题,如何解决。------> 在原本上传的文件的文件名后面+加上+时间戳+哈希值,就避免了文件名的重复了,每个文件都是唯一的了。同时使用 "_" 分割,用于后续,想要获取到真正的文件名,作准备
- 解决文件分目录存放问题,如果将文件都上传到一个目录下,当上传文件很多时,会造成文件速度变慢,因此,可以将文件上传到不同目录,比如一天上传的文件,统一放到一个文件夹 年/月/日,比如 2022/11/11目录.
这里我们创建一个工具类,来解决,返回一个以系统的年月日作为目录的字符串。
java
package com.rainbowsea.springboot.utils;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.SimpleTimeZone;
public class WebUtils {
private WebUtils() {
}
public static String getYearMonthDay() {
// 如何得到当前的日期-》Java基础 日期,三代类
LocalDateTime localDateTime = LocalDateTime.now();
int year = localDateTime.getYear();
int monthValue = localDateTime.getMonthValue();
int dayOfMonth = localDateTime.getDayOfMonth();
String yearMonthDay = year + "-" + monthValue + "-" + dayOfMonth;
return yearMonthDay;
}
/**
* 以年月日创建目录
* 返回一个年月日为格式的目录的字符串
* @return 返回一个年月日为格式的目录的字符串
*/
public static String getUploadFileDirectory() {
// 返回一个年月日为格式的目录的字符串
return "static/images/" + new SimpleDateFormat("yyyy/MM/dd").format(new Date());
}
// 测试一下
public static void main(String[] args) {
System.out.println(WebUtils.getYearMonthDay());
System.out.println(getUploadFileDirectory());
}
}
运行测试:
5. 最后:
"在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。"