• SpringBoot3文件管理


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

    一、简介

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

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

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

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

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

    二、工程搭建

    1、工程结构

    2、依赖管理

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

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
        <version>${spring-boot.version}version>
    dependency>
    
    
    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>easyexcelartifactId>
        <version>${easyexcel.version}version>
        <exclusions>
            <exclusion>
                <groupId>org.slf4jgroupId>
                <artifactId>slf4j-apiartifactId>
            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测试文件批量上传接口:

    四、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 ;
    }
    

    文件效果:

    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 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 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 {
        /**
         * 接收解析的数据块
         */
        @Override
        public void invoke(DataVO data, AnalysisContext context) {
            System.out.println("DataListener:"+data);
        }
        /**
         * 接收解析的表头
         */
        @Override
        public void invokeHeadMap(Map 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 dataList = EasyExcel
                    .read(file.getInputStream(), DataVO.class, new DataListener()).sheet().doReadSync();
            dataList.forEach(System.out::println);
            return "success";
        }
    }
    

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

    五、参考源码

    文档仓库:
    https://gitee.com/cicadasmile/butte-java-note
    
    源码仓库:
    https://gitee.com/cicadasmile/butte-spring-parent
    
  • 相关阅读:
    未来五年,千万不要错过这波红利!
    Springboot整合缓存
    《QDebug 2022年8月》
    Hadoop -- 分布式文件系统
    记一次酣畅淋漓的 K8s Ingress 排错过程(302,404,503,...)
    当下时代背景下,人们要如何看待低代码带来的一切?
    Linux 基础-查看进程命令 ps 和 top
    杰理之蓝牙流程处理,消息处理【篇】
    GBase 8s数据库DB-Access全屏菜单界面介绍(4)
    Vmware虚拟机创建快照、克隆和备份 创建文件夹共享方法
  • 原文地址:https://www.cnblogs.com/cicada-smile/p/17619352.html