• Spring boot接收zip包并获取其中excel文件的方法


    1、问题

     工作中遇到一个需求,接收一个zip包,读取其中的excel文件并处理,解决用户需要多次选择目录和文件的痛点,该zip包包含多级目录

    2、依赖

     需要用到apache的Workbook类来操作Excel,引入以下依赖

    <dependency>
       <groupId>org.apache.poi</groupId>
       <artifactId>poi-ooxml</artifactId>
       <version>3.9</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3、具体实现

    public List<Workbook> getExcelsFromZip(MultipartFile multipartFile) {
            ZipInputStream zis = null;
            List<Workbook> workbooks = new ArrayList<>();
            try {
                // 构建zip流
                zis = new ZipInputStream(multipartFile.getInputStream());
                // 获取文件条目,此方法读取的问zip包中所有条目(目录和文件),多级目录下的所有文件和目录均会被读取
                ZipEntry zipEntry = zis.getNextEntry();
                while (zipEntry != null) {
                    // 判断类型是否为文件
                    if (!zipEntry.isDirectory()) {
                        // 获取文件名
                        String fileName = zipEntry.getName();
                        /* 后缀名 */
                        int suffixIndex = fileName.lastIndexOf(".");
                        String suffix = suffixIndex >= 0 ? fileName.substring(suffixIndex) : "";
                        // 判断是否为excel文件
                        if (".xls".equals(suffix) || ".xlsx".equals(suffix)) {
                            /* 将文件写入到byte数组中 */
                            ByteArrayOutputStream out = new ByteArrayOutputStream();
                            byte[] buffer = new byte[1024];
                            int len;
                            // 在调用了getNextEntry()后,zis就指向了获取到的ZipEntry,读取zis流实际就是读取当前ZipEntry,不会读取到其他条目
                            while ((len = zis.read(buffer)) > 0) {
                                out.write(buffer, 0, len);
                            }
                            // 从输出流中取出byte数组,使用byte数组里的文件数据构建一个输入流
                            ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
                            /* 构建Workbook */
                            Workbook wb;
                            if (".xls".equals(suffix)) {
                                // 老版excel格式,使用HSSFWorkbook
                                wb = new HSSFWorkbook(in);
                            }else {
                                // 新版excel格式,使用XSSFWorkbook
                                wb = new XSSFWorkbook(in);
                            }
                            workbooks.add(wb);
                            // 关闭当前ZipEntry,确保不会重复处理
                            zis.closeEntry();
                            // 获取下一条目
                            zipEntry = zis.getNextEntry();
                        }
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }finally {
                if (zis != null) {
                    try {
                        zis.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            return workbooks;
        }
    
    • 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
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    4、技术要点

    (1)ZipInputStream.getNextEntry()方法

     【1】该方法可获取到zip包内的各级目录及各级目录下的文件,并不需要进行递归处理。
     【2】ZipInputStream实例调用该方法后,将会指向获取到的条目,这时使用ZipInputStream实例操作的就是获取到的ZipEntry对象。可通过ZipInputStream.closeEntry()和ZipInputStream.getNextEntry()迭代到下一个ZipEntry条目。

    (2)为什么选择ByteArrayInputStream和ByteArrayInputStream获取Workbook

     二者是在内存中处理字节的IO流,不同于磁盘IO流会在磁盘中创建临时文件读写,处理速度会更快,在内存中处理数据也符合业务需求。

    (3)ByteArrayInputStream和ByteArrayInputStream不需要关闭吗?

      前面说到这两个流都是在内存中操作数据,没有占用系统的IO资源,所以并不需要显式关闭。查看这两个类的close方法,会发现方法中没有做任何操作。同理,ByteArrayInputStream也不需要进行flush(),该类的flush()方法中也没有进行任何操作。

    (3)为什么不使用Workbook wb = WorkbookFactory.create(zis);直接转化

      实测使用WorkbookFactory.create(zis)会关闭流,导致无法读取后续条目

  • 相关阅读:
    深入Synchronized各种使用方法
    1.5-33:计算分数加减表达式的值
    波浪input输入框文字边框动画
    TDengine 与煤科院五大系统实现兼容性互认,助力煤矿智能化安全体系搭建
    Linux日志管理rsyslog系统日志管理
    总结使人进步,4句真章的理解和实践
    [附源码]java毕业设计在线开放课程平台
    数字金融面板数据:金融效率、数字金融指数、不变价GDP、经济开放度
    oracle 执行计划详解
    Ubuntu下查看资源占用情况
  • 原文地址:https://blog.csdn.net/weixin_48460141/article/details/133326126