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>

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

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)会关闭流,导致无法读取后续条目

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值