SpringBoot之文件上传

作者: adm 分类: java 发布时间: 2021-05-07 16:40

文件上传

文件上传和下载是JAVA WEB中常见的一种操作,文件上传主要是将文件通过IO流传输到服务器的某一个特定的文件夹下;刚开始工作那会一个上传文件常常花费小半天的时间,繁琐的代码量以及XML配置让我是痛不欲生;值得庆幸的是有了Spring Boot短短的几句代码就能实现文件上传与本地写入操作….

导入依赖

在 pom.xml 中添加上 spring-boot-starter-web 和 spring-boot-starter-thymeleaf 的依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

配置文件

默认情况下 Spring Boot 无需做任何配置也能实现文件上传的功能,但有可能因默认配置不符而导致文件上传失败问题,所以了解相关配置信息更有助于我们对问题的定位和修复;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 禁用 thymeleaf 缓存
spring.thymeleaf.cache=false
# 是否支持批量上传   (默认值 true)
spring.servlet.multipart.enabled=true
# 上传文件的临时目录 (一般情况下不用特意修改)
spring.servlet.multipart.location=
# 上传文件最大为 1M (默认值 1M 根据自身业务自行控制即可)
spring.servlet.multipart.max-file-size=1048576
# 上传请求最大为 10M(默认值10M 根据自身业务自行控制即可)
spring.servlet.multipart.max-request-size=10485760
# 文件大小阈值,当大于这个阈值时将写入到磁盘,否则存在内存中,(默认值0 一般情况下不用特意修改)
spring.servlet.multipart.file-size-threshold=0
# 判断是否要延迟解析文件(相当于懒加载,一般情况下不用特意修改)
spring.servlet.multipart.resolve-lazily=false

如默认只允许1M以下的文件,当超出该范围则会抛出下述错误

1
2
org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: 
the request was rejected because its size (20738021) exceeds the configured maximum (10485760)

具体代码

上传页面

在 src/main/resources 目录下新建 static 目录和 templates 目录。在 templates 中新建一个 index.html 的模板文件;此处实现 单文件上传多文件上传BASE64编码 三种上传方式,其中 BASE64 的方式在对Android/IOS/H5等方面还是不错的…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>

<h2>单一文件上传示例</h2>
<div>
    <form method="POST" enctype="multipart/form-data" action="/uploads/upload1">
        <p>
            文件1:<input type="file" name="file"/>
            <input type="submit" value="上传"/>
        </p>
    </form>
</div>

<hr/>
<h2>批量文件上传示例</h2>

<div>
    <form method="POST" enctype="multipart/form-data"
          action="/uploads/upload2">
        <p>
            文件1:<input type="file" name="file"/>
        </p>
        <p>
            文件2:<input type="file" name="file"/>
        </p>
        <p>
            <input type="submit" value="上传"/>
        </p>
    </form>
</div>

<hr/>
<h2>Base64文件上传</h2>
<div>
    <form method="POST" action="/uploads/upload3">
        <p>
            BASE64编码:<textarea name="base64" rows="10" cols="80"></textarea>
            <input type="submit" value="上传"/>
        </p>
    </form>
</div>

</body>
</html>

控制层

创建一个FileUploadController,其中@GetMapping的方法用来跳转index.html页面,而@PostMapping相关方法则是对应的 单文件上传多文件上传BASE64编码 三种处理方式。

@RequestParam("file") 此处的"file"对应的就是html 中 name="file" 的 input 标签,而将文件真正写入的还是借助的commons-io中的FileUtils.copyInputStreamToFile(inputStream,file)

 

package cn.ywstore.qy.modules.admin.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.util.Base64Utils;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
@Slf4j
@RequestMapping("/admin/login")
public class UploadsController {
    @GetMapping("/uploads")
    public String uploads(){
        return "uploads";
    }
    @PostMapping("/upload1")
    @ResponseBody
    public Map<String, String> upload1(@RequestParam("file") MultipartFile file) throws IOException {
        log.info("[文件类型] - [{}]", file.getContentType());
        log.info("[文件名称] - [{}]", file.getOriginalFilename());
        log.info("[文件大小] - [{}]", file.getSize());
        // TODO 将文件写入到指定目录(具体开发中有可能是将文件写入到云存储/或者指定目录通过 Nginx 进行 gzip 压缩和反向代理,此处只是为了演示故将地址写成本地电脑指定目录)
        // 文件输出地址
        String filePath = "H:/java/aaa/bbb/ccc/" ;
        new File(filePath).mkdirs();
        String filename = file.getOriginalFilename();//获取文件名称
        String suffixname = filename.substring(filename.lastIndexOf("."));//后缀
        filename = System.currentTimeMillis() + suffixname;//文件上传后重命名数据库存储
        file.transferTo(new File(filePath + filename));
        Map<String, String> result = new HashMap<>(16);
        result.put("contentType", file.getContentType());
        result.put("fileName", filename);
        result.put("fileSize", file.getSize() + "");
        return result;
    }

    @PostMapping("/upload2")
    @ResponseBody
    public List<Map<String, String>> upload2(@RequestParam("file") MultipartFile[] files) throws IOException {
        if (files == null || files.length == 0) {
            return null;
        }
        List<Map<String, String>> results = new ArrayList<>();
        for (MultipartFile file : files) {
            // TODO Spring Mvc 提供的写入方式
            file.transferTo(new File("H:\\java\\" + file.getOriginalFilename()));
            Map<String, String> map = new HashMap<>(16);
            map.put("contentType", file.getContentType());
            map.put("fileName", file.getOriginalFilename());
            map.put("fileSize", file.getSize() + "");
            results.add(map);
        }
        return results;
    }

    @PostMapping("/upload3")
    @ResponseBody
    public void upload2(String base64) throws IOException {
        // TODO BASE64 方式的 格式和名字需要自己控制(如 png 图片编码后前缀就会是 data:image/png;base64,)
        final File tempFile = new File("F:\\app\\chapter16\\test.jpg");
        // TODO 防止有的传了 data:image/png;base64, 有的没传的情况
        String[] d = base64.split("base64,");
        final byte[] bytes = Base64Utils.decodeFromString(d.length > 1 ? d[1] : d[0]);
        FileCopyUtils.copy(bytes, tempFile);

    }
}

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!