使用 POI 封装工具类处理 Excel 表格文件 -- 导入篇

本贴最后更新于 1419 天前,其中的信息可能已经时异事殊

使用 POI 封装工具类处理 Excel 表格文件--导入篇

有关于系统中导入 Excel 表格文件,其实大家普遍见过或者使用过。但是相较于网上提供的 EasyPOI 或者 Hutool 工具类里面封装好的 Excel 处理方法(马总推荐使用的工具类),其实在很多情况下不太符合我们的导出或者导入场景,总是需要手动处理下,很是繁琐,毕竟不是所有的表格数据我们都需要,或不是所有的数据都需要我们导出。所以这里手动处理下 POI 类(其实人家封装的已经很完整了,然而还是不太好用),让这个处理的方法更贴合实际使用。

云中谁寄锦书来?雁字回时,月满西楼。

构思

我们使用 Excel 导入的目的是什么?

  • 通常我们使用 Excel 导入的目的说白了就是为了获取表格里面的数据,然后整理后提炼出来被我们使用或者存入到数据库中。但是很多情况下,不是所有的列数据都是我们需要的,所以我们首先要明确我们需要什么。我这里处理使用常量来定义了导入的模板。(当然大家还可以使用枚举,或者选择和前端交互,肆意而为,毕竟办法总比困难多-狗头保命trollface

模板定义: 0| 备注(属性名)| 属性名

 public static class TempLate{
        @ConstantAnnotation("001")
        public static final String MODE_CONTENT="0|凭证号|voucherNo,1|会计年度|accountYear,2|会计月度|accountMonth,3|数据来源|dataSource,4|存储类型(0. 电子 1. 电子和纸质)|saveType,5|存储类型名称|voucherName,6|摘要|accountAbstract,7|关键字|keyWord,8|凭证类型|voucherTypeId,9|凭证类型名称|voucherTypeName,10|凭证状态0 待装册 1 已装册|voucherState,11|创建时间|creatTime,12|单位编码|orgCode,13|单位名称|orgName";
    }

知道了我们要获取的数据有哪些后,我们怎么做匹配?

  • 当我们知道了我们需要表格数据的哪一列,我们心中其实也就明白了这列数据对应我们数据模型的哪一个字段,这样在后面处理的时候只要匹配成功即可。
    所以我们需要用到反射的一点点知识,使用好 Field,然后简单的做几个判断,就可以完美匹配了。
 T object = instance.newInstance();
 Field[] fields= object.getClass().getDeclaredFields();

注:instance 是一个 Class 类型的入参,用来放我们需要储存数据的数据模型类.class 还有就是 fields,是我们.class 里面的所有属性。(获取私有属性要把 accessible 赋值 true)

  • 接下来我们拿到属性数组后,可以循环比较类型或者属性名来操作并赋值。
if (mapHeader.get(s).equals(f.getName())) {
     //获取私有变量
    f.setAccessible(true); 
    f.set(object, cell.getStringCellValue());

如何调用?我们该传什么进行处理?

  • 首先传一个要处理的 Excel 文件,然后我们传个模板,再传一个我们想要返回结果集合的类型.class 即可。例:
List<EaVoucher> list=POIUtils.excelToEmployee(file,EaVoucher.class,modeText);

模板处理

public static Map<String, String> stringToMap(String text) throws Exception {
        Map<String, String> mapHeader = new HashMap<>();
        String[] decollator = text.split(",");
        if (decollator.length > 0) {
            for (String s : decollator) {
                String[] desc = s.split("\\|");
                if (desc.length < 3 || "".equals(desc[0]) || "".equals(desc[2])) {
                    throw new Exception("模板格式有误,请检查!");
                }
                mapHeader.put(desc[0], desc[2]);
            }
        }
        return mapHeader;
    }

然后我们就可以使用这个工具类了。源码方法如下:

 /** 
    * @Description:  excel文件解析成相应类型的List
    * @Param: [file, instance, modeText] 
    * @return: java.util.List<T> 
    * @Author: ShiDunKai 
    * @Date: 2020/5/29 
    */ 
    public static <T> List<T> excelToEmployee(MultipartFile file, Class<T> instance, String modeText) throws Exception {
        //获取导入模板
        Map<String, String> mapHeader = stringToMap(modeText);
        //接收list
        List<T> tList = new ArrayList<>();
        //创建一个workbook对象
        HSSFWorkbook workbook = new HSSFWorkbook(file.getInputStream());
        //获取workbook中标签页的数量
        int numberOfSheets = workbook.getNumberOfSheets();
        for (int sheetNumber = 0; sheetNumber < numberOfSheets; sheetNumber++) {
            HSSFSheet sheetAt = workbook.getSheetAt(sheetNumber);
            //获取行
            int physicalNumberOfRows = sheetAt.getPhysicalNumberOfRows();
            for (int rowNumber = 0; rowNumber < physicalNumberOfRows; rowNumber++) {
                if (0 == rowNumber) {
		//跳过标题行 因为我们这里不做任何处理,所以有没有continue,效果都是一样的,精简才是王道。
                } else {
                    //解析第一行,与模板进行匹配
                    HSSFRow row = sheetAt.getRow(rowNumber);
                    //空行判断
                    if (null == row) {
                        continue;
                    }
                    //一行一个对象
                    T object = instance.newInstance();
                    Field[] fields = object.getClass().getDeclaredFields();
                    //获取列
                    int physicalNumberOfCells = row.getPhysicalNumberOfCells();
                    if (physicalNumberOfCells >= mapHeader.size()) {
                        for (String s : mapHeader.keySet()) {
                            HSSFCell cell = row.getCell(Integer.parseInt(s));
                            for (Field f:fields) {
                                if (mapHeader.get(s).equals(f.getName())) {
                                    //获取私有变量
                                    f.setAccessible(true);
                                    f.set(object, cell.getStringCellValue());
                                } else {
                                    throw new Exception("模板属性名与对象属性不符,请检查模板是否正确配置。");
                                }
                            }
                        }
                    } else {
                        throw new Exception("表格列数量与实际要导出的列数不符,无法获取某些列数据,请检查模板或者表格文件是否正确。");
                    }
                    tList.add(object);
                }
            }
        }
        return tList;
    }

后:

是不是很简单,模板我们可以随时调整,如果后期需要做集成,我们完全可以想办法和前端进行交互,使工具类的扩展性再提一个层次。前期业务量小的话完全可以自定义模板,不影响使用效果的。😋

至于导入我们如何做处理,其实大致思路和上面是一样的,只不过现在已经半夜 0:08,早睡保命要紧,明天还要早起吃早点,还要工作。。。那就拖到周六日给大家分享吧(或许拖更久,别急:trollface: )

其实这个博客在电脑上看效果是最好的,但是大家平时还是看手机比较多,所以微信公众号随后也会完成更新推送,欢迎关注留言讨论(留言了我也不一定回复)。
贴图:
qrcodeforgh32622caac87c2581.jpg

  • POI
    22 引用 • 21 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3167 引用 • 8207 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...