• 作者:老汪软件技巧
  • 发表时间:2024-09-14 07:01
  • 浏览量:

1. 补:Spring Boot 当中实现文件上传功能(附+源代码)

@[toc]

2. 准备工作

我们在 pom.xml 文件当中导入相关的 jar 依赖。如下:

"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.0modelVersion>
    <groupId>com.rainbowseagroupId>
    <artifactId>springboot-fileUploadartifactId>
    <version>1.0-SNAPSHOTversion>
    
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.5.3version>
    parent>
    
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-thymeleafartifactId>
        dependency>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>
    dependencies>
project>

编写项目的场景启动器:

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 视图解析器。所以关于前端映射文件的存放位置,不可以随便放。

enctype="multipart/form-data" 文件提交
multiple 属性可以选中多个文件信息上传
<input type="reset" value="重新填写"><br> ; type="reset" 清空表单,重新填写表单

前端页面显示效果:

3. 开始演示:实现文件上传功能(附+源码)3.1 第一种方式:静态创建方式

静态创建方式: 静态方式,先手动在指定的 windows 当中创建好目录,再放入上传的文件。到其目录当中。

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 "注册用户成功/文件上传成功";
    }
}

运行测试::8080/upload.html

3.2 第二种方式:动态创建方式

动态创建方式: 以项目作为根路径创建目录,存放文件。这种方式兼容性更好,没有盘符之说。可移植性更好。

动态创建的核心,就是获取到项目的根路径,从而以项目的根路径,作为参照/相对路径,从而创建存放上传文件的目录。

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 进行修改

spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 50MB

解决文件覆盖问题,如果文件名相同,会出现覆盖问题,如何解决。——> 在原本上传的文件的文件名后面+加上+时间戳+哈希值,就避免了文件名的重复了,每个文件都是唯一的了。同时使用 ”_“ 分割,用于后续,想要获取到真正的文件名,作准备

解决文件分目录存放问题,如果将文件都上传到一个目录下,当上传文件很多时,会造成文件速度变慢,因此,可以将文件上传到不同目录,比如一天上传的文件,统一放到一个文件夹 年/月/日,比如 2022/11/11目录.

这里我们创建一个工具类,来解决,返回一个以系统的年月日作为目录的字符串。

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. 最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”

在这里插入图片描述