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)) {
                        /* 读取文件 */
                        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);
                        }
                        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;
    }

3、技术要点

(1)ZipInputStream.getNextEntry()方法

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

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

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

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

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

你可能感兴趣的:(开发日记,spring,boot,excel,后端)