SpringBoot(八)SpringBoot中文件上传案例

830 阅读3分钟

本文以 SpringBoot 实现一个 文件上传的简单操作,可实现控制文件上传大小,文件上传路径。

1、准备工作

创建一个SpringBoot项目,创建SpringBoot 项目同时添加 webapp 文件夹 用于放 html 页面。

项目结构:

可能你创建的SpringBoot项目没有webapp文件夹,我也为你准备了解决办法。


解决方法:

点击工具栏 Project Structure 按钮微信截图_20200825164438,在打开的窗口中选中 Moudles -> Web,按照步骤添加配置:

项目有webapps文件夹了吧,继续来看文件上传吧!↓


2、添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
<!--web模块-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- thymeleaf模板引擎 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

完整pom.xml,在build中添加resource保证在webapps下面的html文件进行编译。

<?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.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ifknow</groupId>
    <artifactId>spring-boot-fileupload</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-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</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!--web模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- thymeleaf模板引擎 -->
        <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>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/webapp</directory>
                <!--注意此次必须要放在此目录下才能被访问到 -->
                <targetPath>META-INF/resources</targetPath>
                <includes>
                    <include>**/**</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>**/*</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>

3、配置application.yml文件

配置项目上传路径和文件上传大小。

server:
  port: 8081
# 文件上传路径配置
my:
  uploadFile:
    uploadPath: C:\softInstall\Tomcat 8.5\webapps\fileupload\
    readPath: \fileupload\
# 文件上传大小
spring:
  servlet:
    multipart:
      enabled: true
      file-size-threshold: 0
      max-file-size: 200MB
      max-request-size: 200MB

4、编写一个html页面

编写一个HTML页面,页面上一个file的button,为了展现技术,很无聊的加了一个简陋的进度条。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<style>
    .box {
        height: 30px;
        width: 200px;
        border: 1px solid green;
        margin-top: 30px;
    }

    article {
        height: 100%;
        width: 0;
        background: palegreen;
    }
</style>
<body>
<input type="file" id="file">
<div class="box">
    <article class="cont"></article>
</div>
<a id="back" href="#">##</a>
</body>
<script>
    function uploadImg(fileInfo) {
        const data = new FormData();
        data.append('file', fileInfo)
        return new Promise((reslove, reject) => {
            $.ajax({
                type: 'post',
                url: '/file/upload',
                data,
                processData: false,
                contentType: false,
                xhr() {
                    const myXhr = $.ajaxSettings.xhr();
                    myXhr.upload.addEventListener('progress', (e) => {
                        console.log(e.loaded, e.total)
                        document.querySelector('.cont').style.width = `${(e.loaded / e.total) * 100}%`
                    })
                    return myXhr
                },
                success(data) {
                    console.log(data.url)
                    $("#back").attr("href", "http://localhost:8080" + data.url)
                    $("#back").text(data.newName)
                },
                error() {
                }
            })
        })
    }

    const file = document.querySelector('#file');
    file.onchange = function (e) {
        document.querySelector('.cont').style.width = '0px'
        const files = e.target.files;
        [...files].forEach((res) => {
            uploadImg(res).then((res) => {
            }, (err) => {
            })
        })
    }
</script>
</html>

5、后台实现

  • 读取application.yml的配置文件中定义的内容,编写一个配置类
package com.ifknow.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: ifknow <br>
 * @Date: 2020/8/25  10:18 <br>
 * @Description: 读取配置文件中定义的文件上传路径
 */
@Configuration
@ConfigurationProperties(prefix = "my", ignoreUnknownFields = false)
public class ObtainProperties {
    /**
     * 文件上传配置
     */
    private final UploadFile uploadFile = new UploadFile();

    public UploadFile getUploadFile() {
        return uploadFile;
    }

    @Data
    public static class UploadFile {
        private String uploadPath;
        private String readPath;
    }
}
  • 文件上传实现
package com.ifknow.utils;

import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @Author: ifknow <br>
 * @Date: 2020/8/25  10:28 <br>
 * @Description: NO Description
 */
public class FileUtil {

    /**
     * 上传文件接口
     *
     * @param file    文件
     * @param request
     * @param model
     * @return
     */
    public Map<String, Object> upload(MultipartFile file, HttpServletRequest request, FileModel model) {
        //文件不为空
        if (!file.isEmpty()) {
            //上传文件名
            String fileName = file.getOriginalFilename();
            String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
            try {
                Map<String, Object> map = this.toUpload(file, suffix, fileName, request, model);
                return map;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 上传操作
     *
     * @param file     文件
     * @param suffix   后缀
     * @param fileName 文件名
     * @param request
     * @param model
     * @return
     */
    public Map<String, Object> toUpload(MultipartFile file, String suffix, String fileName, HttpServletRequest request,FileModel model) {
        suffix = suffix.toLowerCase();
        Map<String, Object> pathMap = this.filePath(suffix, model);
        String filePath = pathMap.get("filePath").toString();
        File realPath = new File(filePath, fileName);
        if (!realPath.getParentFile().exists()) {
            realPath.getParentFile().mkdirs();
        }
        String newName = UUID.randomUUID().toString().replace("-", "") + fileName.substring(fileName.lastIndexOf("."));
        File newFile = new File(filePath + File.separator + newName);
        try {
            file.transferTo(newFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String fileType = pathMap.get("fileType").toString();
        Map<String, Object> map = new HashMap<String, Object>();
        //后缀
        map.put("suffix", suffix);
        //原文件名
        map.put("oldName", fileName.substring(0, fileName.lastIndexOf(".")));
        //新文件名
        map.put("newName", newName.substring(0, newName.lastIndexOf(".")));
        //文件大小
        map.put("fileSize", file.getSize());
        //路径
        map.put("url", model.getReadPath() + fileType + File.separator + newName);
        // 类型(0视/1文/2图片)
        map.put("type", getTypeByUrl(suffix));
        return map;
    }

    /**
     * 判断文件的类型:图片、视频、word。。。返回路径
     *
     * @param suffix 后缀名
     * @return
     */
    public Map<String, Object> filePath(String suffix, FileModel model) {
        Map<String, Object> map = new HashMap<String, Object>();
        String filePath = "";
        String fileType = "";
        if (suffix.equals("jpg") || suffix.equals("png")) {
            fileType = "images";
        } else if (suffix.equals("mov")) {
            fileType = "mov";
        } else if (suffix.equals("mp4")) {
            fileType = "mp4";
        } else if (suffix.equals("xlsx") || suffix.equals("xls")) {
            fileType = "excel";
        } else if (suffix.equals("pdf")) {
            fileType = "pdf";
        } else if (suffix.equals("ppt") || suffix.equals("pptx")) {
            fileType = "ppt";
        } else if (suffix.equals("doc") || suffix.equals("docx")) {
            fileType = "word";
        } else {
            fileType = "other";
        }
        filePath = model.getUploadPath() + fileType;
        map.put("filePath", filePath);
        map.put("fileType", fileType);
        return map;
    }

    public int getTypeByUrl(String suffix) {
        suffix = suffix.toLowerCase().trim();
        if (!"jpg".equals(suffix) && !"png".equals(suffix)) {
            if ("mov".equals(suffix) || "mp4".equals(suffix)) {
                return 0;
            } else {
                return 1;
            }
        }
        return 2;
    }
}
package com.ifknow.utils;

/**
 * @Author: ifknow <br>
 * @Date: 2020/8/25  10:00 <br>
 * @Description: NO Description
 */
public class FileModel {
    private String uploadPath;
    private String readPath;

    public String getUploadPath() {
        return uploadPath;
    }

    public FileModel setUploadPath(String uploadPath) {
        this.uploadPath = uploadPath;
        return this;
    }

    public String getReadPath() {
        return readPath;
    }

    public FileModel setReadPath(String readPath) {
        this.readPath = readPath;
        return this;
    }
}
  • 编写controller测试
package com.ifknow.controller;

import com.ifknow.config.ObtainProperties;
import com.ifknow.utils.FileModel;
import com.ifknow.utils.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: ifknow <br>
 * @Date: 2020/8/25  9:59 <br>
 * @Description: NO Description
 */
@Slf4j
@CrossOrigin
@RestController
@RequestMapping("/file")
public class FileUploadController {

    @Autowired
    private ObtainProperties obtainProperties;

    @PostMapping("/upload")
    public Map<String, Object> upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
        FileModel model = new FileModel().setUploadPath(obtainProperties.getUploadFile().getUploadPath()).setReadPath(obtainProperties.getUploadFile().getReadPath());
        FileUtil uploadUtil = new FileUtil();
        Map<String, Object> map = uploadUtil.upload(file, request, model);
        log.info(file.getOriginalFilename() + "  上传成功");
        return map;
    }
}

6、接口调试

打开浏览器访问 http://localhost:8081/index.html

选择文件后,可看到效果,接口返回的文件信息,在实际的开发中可用于图片回显等业务需求。

本文中暂未考虑到异常处理,可以自己了解。方案:可以使用@RestControllerAdcvice。


示例代码-GitHub

示例代码-Gitee

个人博客-ifknow