SpringBoot3文件管理

本文涉及的产品
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 项目中文件管理是常见的复杂功能,文件的类型多样,处理起来比较复杂,其次文件涉及大量的IO操作,容易引发内存溢出;

标签:上传.下载.Excel.导入.导出;

一、简介

在项目中,文件管理是常见的复杂功能;

首先文件的类型比较多样,处理起来比较复杂,其次文件涉及大量的IO操作,容易引发内存溢出;

不同的文件类型有不同的应用场景;

比如:图片常用于头像和证明材料;Excel偏向业务数据导入导出;CSV偏向技术层面数据搬运;PDF和Word用于文档类的材料保存等;

下面的案例只围绕普通文件Excel两种类型进行代码实现;

二、工程搭建

1、工程结构

1.png

2、依赖管理

普通文件的上传下载,依赖spring-boot框架即可,而Excel类型选择easyexcel组件,该组件内部依赖了apache-poi组件的4.1.2版本;

<!-- 基础框架组件 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>${spring-boot.version}</version>
</dependency>

<!-- Excel组件 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>${easyexcel.version}</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

三、上传下载

1、配置管理

在配置文件中,添加max-file-size单个文件大小限制和max-request-size请求最大限制两个核心参数;

需要说明的一点是:如何设定参数值的大小,与业务场景和服务器的处理能力都有关系,在测试的过程中优化即可;

spring:
  # 文件配置
  servlet:
    multipart:
      enabled: true
      # 文件单个限制
      max-file-size: 10MB
      # 请求最大限制
      max-request-size: 20MB

2、上传下载

这里提供一个文件批量上传接口和一个文件下载接口,把文件管理在工程中的resources/file目录下,下载接口中需要指定该目录下的文件名称;

@RestController
public class FileWeb {
   
   
    private static final Logger logger = LoggerFactory.getLogger(FileWeb.class);
    @Resource
    private FileService fileService ;

    /**
     * 文件上传
     */
    @PostMapping("/file/upload")
    public String upload (HttpServletRequest request,
                          @RequestParam("file") MultipartFile[] fileList) throws Exception {
   
   
        String uploadUser = request.getParameter("uploadUser");
        if (uploadUser.isEmpty()){
   
   
            return "upload-user is empty";
        }
        logger.info("upload-user:{}",uploadUser);
        for (MultipartFile multipartFile : fileList) {
   
   
            // 解析文件信息和保存
            fileService.dealFile(multipartFile);
        }
        return "success" ;
    }
    /**
     * 文件下载
     */
    @GetMapping("/file/download")
    public void upload (@RequestParam("fileName") String fileName,
                        HttpServletResponse response) throws Exception {
   
   
        if (!fileName.isBlank()){
   
   
            String filePath = ResourceUtils.getURL("m1-04-boot-file/src/main/resources/file").getPath();
            File file = new File(filePath,fileName) ;
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
            response.setContentType("application/octet-stream");
            Files.copy(Paths.get(file.getPath()), response.getOutputStream());
        }
    }
}

/**
 * 文件服务类
 */
@Service
public class FileService {
   
   

    private static final Logger logger = LoggerFactory.getLogger(FileService.class);

    public void dealFile (MultipartFile multipartFile) throws Exception {
   
   
        logger.info("Name >> {}",multipartFile.getName());
        logger.info("OriginalFilename >> {}",multipartFile.getOriginalFilename());
        logger.info("ContentType >> {}",multipartFile.getContentType());
        logger.info("Size >> {}",multipartFile.getSize());
        // 文件输出地址
        String filePath = ResourceUtils.getURL("m1-04-boot-file/src/main/resources/file").getPath();
        File writeFile = new File(filePath, multipartFile.getOriginalFilename());
        multipartFile.transferTo(writeFile);
    }
}

使用Postman测试文件批量上传接口:

2.png

四、Excel文件

1、Excel创建

基于easyexcel组件中封装的EasyExcel工具类,继承自EasyExcelFactory工厂类,实现Excel单个或多个Sheet的创建,并且在单个Sheet中写多个Table数据表;

@Service
public class ExcelService {
   
   
    /**
     * Excel-写单个Sheet
     */
    public static void writeSheet () throws Exception {
   
   
        // 文件处理
        String basePath = getAbsolutePath();
        File file = new File(basePath+"/easy-excel-01.xlsx") ;
        checkOrCreateFile(file);
        // 执行写操作
        EasyExcel.write(file).head(DataVO.class)
                .sheet(0,"用户信息").doWrite(DataVO.getSheet1List());
    }
    /**
     * Excel-写多个Sheet
     */
    public static void writeSheets () throws Exception {
   
   
        // 文件处理
        String basePath = getAbsolutePath();
        File file = new File(basePath+"/easy-excel-02.xlsx") ;
        checkOrCreateFile(file);
        ExcelWriter excelWriter = null;
        try {
   
   
            excelWriter = EasyExcel.write(file).build();
            // Excel-Sheet1
            WriteSheet writeSheet1 = EasyExcel.writerSheet(0,"分页1").head(DataVO.class).build();
            // Excel-Sheet2
            WriteSheet writeSheet2 = EasyExcel.writerSheet(1,"分页2").head(DataVO.class).build();
            // Excel-Sheet3,写两个Table
            WriteSheet writeSheet3 = EasyExcel.writerSheet(2,"分页3").build();
            WriteTable dataTable = EasyExcel.writerTable(0).head(DataVO.class).build();
            WriteTable dataExtTable = EasyExcel.writerTable(1).head(DataExtVO.class).build();
            // 执行写操作
            excelWriter.write(DataVO.getSheet1List(), writeSheet1);
            excelWriter.write(DataVO.getSheet2List(), writeSheet2);
            excelWriter.write(DataVO.getSheet1List(),writeSheet3,dataTable) ;
            excelWriter.write(DataExtVO.getSheetList(),writeSheet3,dataExtTable) ;
        } catch (Exception e){
   
   
            e.printStackTrace();
        } finally {
   
   
            if (excelWriter != null){
   
   
                excelWriter.close();
            }
        }
    }
}

/**
 * 实体类,这里的注解会解析为Excel中的表头
 */
public class DataVO {
   
   
    @ExcelProperty("编号")
    private Integer id ;
    @ExcelProperty("名称")
    private String name ;
    @ExcelProperty("手机号")
    private String phone ;
    @ExcelProperty("城市")
    private String cityName ;
    @ExcelProperty("日期")
    private Date date ;
}

文件效果:

3.png

2、Excel读取

对于读取Excel文件来说,则需要根据具体的样式来定了,在easyexcel组件中还可以添加读取过程的监听器;

@Service
public class ExcelService {
   
   
    /**
     * Excel-读取数据
     */
    public static void readExcel () throws Exception {
   
   
        // 文件处理
        String basePath = getAbsolutePath();
        File file = new File(basePath+"/easy-excel-01.xlsx") ;
        if (!file.exists()){
   
   
            return ;
        }
        // 读取数据
        List<DataVO> dataList = EasyExcel.read(file).head(DataVO.class)
                .sheet(0).headRowNumber(1).doReadSync();
        dataList.forEach(System.out::println);
    }
    /**
     * Excel-读取数据使用解析监听器
     */
    public static void readExcelListener () throws Exception {
   
   
        // 文件处理
        String basePath = getAbsolutePath();
        File file = new File(basePath+"/easy-excel-01.xlsx") ;
        if (!file.exists()){
   
   
            return ;
        }
        // 读取数据,并且使用解析监听器
        DataListener dataListener = new DataListener() ;
        List<DataVO> dataSheetList = EasyExcel.read(file,dataListener).head(DataVO.class)
                .sheet(0).headRowNumber(1).doReadSync();
        dataSheetList.forEach(System.out::println);
    }
}

3、解析监听

继承AnalysisEventListener类,并重写其中的方法,可以监听Excel的解析过程,或者添加一些自定义的处理逻辑;

public class DataListener extends AnalysisEventListener<DataVO> {
   
   
    /**
     * 接收解析的数据块
     */
    @Override
    public void invoke(DataVO data, AnalysisContext context) {
   
   
        System.out.println("DataListener:"+data);
    }
    /**
     * 接收解析的表头
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
   
   
        System.out.println("DataListener:"+headMap);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
   
   
        System.out.println("DataListener:after...all...analysed");
    }
}

4、导入导出

实际上Excel文件的导入导出,原理与文件的上传下载类似,只不过这里使用easyexcel组件中的API来直接处理Excel的写和读;

@RestController
public class ExcelWeb {
   
   

    @GetMapping("excel/download")
    public void download(HttpServletResponse response) throws IOException {
   
   
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode("Excel数据", StandardCharsets.UTF_8).replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), DataVO.class).sheet("用户").doWrite(DataVO.getSheet1List());
    }

    @ResponseBody
    @PostMapping("excel/upload")
    public String upload(@RequestParam("file") MultipartFile file) throws IOException {
   
   
        List<DataVO> dataList = EasyExcel
                .read(file.getInputStream(), DataVO.class, new DataListener()).sheet().doReadSync();
        dataList.forEach(System.out::println);
        return "success";
    }
}

使用Postman测试单个Excel上传接口:

4.png

五、参考源码

文档仓库:
https://gitee.com/cicadasmile/butte-java-note

源码仓库:
https://gitee.com/cicadasmile/butte-spring-parent
相关实践学习
DataV Board用户界面概览
本实验带领用户熟悉DataV Board这款可视化产品的用户界面
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
相关文章
|
3月前
|
存储 Java Spring
Spring Boot中的应用配置文件管理
Spring Boot中的应用配置文件管理
|
存储 算法 Java
基于Springboot+Mybatis+Mysql的网盘文件管理系统
基于Springboot+Mybatis+Mysql的网盘文件管理系统
211 0
基于Springboot+Mybatis+Mysql的网盘文件管理系统
|
Web App开发 JavaScript 前端开发
SpringBoot之前端文件管理
WebJars能使Maven的依赖管理支持OSS的JavaScript库/CSS库,比如jQuery、Bootstrap等。 (1)添加js或者css库 pom.xml  Xml代码          org.
1222 0
|
8天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
62 1
|
1月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的服装商城管理系统
基于Java+Springboot+Vue开发的服装商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的服装商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
100 2
基于Java+Springboot+Vue开发的服装商城管理系统
|
1月前
|
前端开发 JavaScript Java
SpringBoot项目部署打包好的React、Vue项目刷新报错404
本文讨论了在SpringBoot项目中部署React或Vue打包好的前端项目时,刷新页面导致404错误的问题,并提供了两种解决方案:一是在SpringBoot启动类中配置错误页面重定向到index.html,二是将前端路由改为hash模式以避免刷新问题。
140 1
|
9天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用
【10月更文挑战第8天】本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,通过 Spring Initializr 创建并配置 Spring Boot 项目,实现后端 API 和安全配置。接着,使用 Ant Design Pro Vue 脚手架创建前端项目,配置动态路由和菜单,并创建相应的页面组件。最后,通过具体实践心得,分享了版本兼容性、安全性、性能调优等注意事项,帮助读者快速搭建高效且易维护的应用框架。
18 3
|
1月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的大学竞赛报名管理系统
基于Java+Springboot+Vue开发的大学竞赛报名管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的大学竞赛报名管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
129 3
基于Java+Springboot+Vue开发的大学竞赛报名管理系统
|
1月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的蛋糕商城管理系统
基于Java+Springboot+Vue开发的蛋糕商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的蛋糕商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
90 3
基于Java+Springboot+Vue开发的蛋糕商城管理系统
|
1月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的美容预约管理系统
基于Java+Springboot+Vue开发的美容预约管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的美容预约管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
42 3
基于Java+Springboot+Vue开发的美容预约管理系统