• EasyExcel——读Excel


    easyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。
    64M内存1分钟内读取75M(46W行25列)的Excel(当然还有急速模式能更快,但是内存占用会在100M多一点)
    可有效避免OOM。
    致敬阿里:

    ---参照官方文档进行编辑,主要记录了工作中用到的,用的少的就没有记录
    ---官方文档 : https://www.yuque.com/easyexcel
    ---官方github : https://github.com/alibaba/easyexcel

    操作实体类

    @Data
    public class DemoData {
        @ExcelProperty("字符串标题")
        private String stringData;
        @ExcelProperty(value = "数字标题")
        private Integer  integerData;
        @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
        @ExcelProperty("日期标题")
        private Date  dateData;
        @NumberFormat("#.##%")
        @ExcelProperty("浮点型标题")
        private Double   dounleData;
    }
    

    监听器类

    //DemoDataListener 不能被spring管理,要每次读取excel都要new,因此注入其他类的时候需要通过构造方法进行,而不是通过容器
    public class DemoDataListener  extends AnalysisEventListener<DemoData> {
        private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
        /**
         * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
         */
        private DemoDAO demoDAO;
        public DemoDataListener() {
            // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
            demoDAO = new DemoDAO();
        }
        /**
         * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
         * @param demoDAO
         */
        public DemoDataListener(DemoDAO demoDAO) {
            this.demoDAO = demoDAO;
        }
        /**
         * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
         */
        private static final int BATCH_COUNT = 5;
        List<DemoData> list = new ArrayList<DemoData>();
        /*
         *  每解析一条数据就会来调用
         */
        @Override
        public void invoke(DemoData demoData, AnalysisContext analysisContext) {
            LOGGER.info("解析到一条数据:{}", JSON.toJSONString(demoData));
            //根据demoData对数据进行校验
            list.add(demoData);
            // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
            if (list.size() >= BATCH_COUNT) {
                saveData();
                // 存储完成清理 list
                list.clear();
            }
        }
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            // 这里也要保存数据,确保最后遗留的数据也存储到数据库
            saveData();
            LOGGER.info("所有数据解析完成!");
        }
        /**
         * 加上存储数据库
         */
        private void saveData() {
            LOGGER.info("{}条数据,开始存储数据库!", list.size());
            demoDAO.save(list);
            LOGGER.info("存储数据库成功!");
        }
    
    }
    

    dao层(逗你玩~~)

    /**
     * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
     **/
    public class DemoDAO {
        public void save(List<DemoData> list) {
            // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
        }
    }
    

    读示例

            String fileName = "c://***";
            EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); 
    
    2020-04-13 19:26:37.109  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 解析到一条数据:{}
    2020-04-13 19:26:37.111  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 解析到一条数据:{}
    2020-04-13 19:26:37.111  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 解析到一条数据:{}
    2020-04-13 19:26:37.112  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 解析到一条数据:{}
    2020-04-13 19:26:37.112  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 解析到一条数据:{}
    2020-04-13 19:26:37.112  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 5条数据,开始存储数据库!
    2020-04-13 19:26:37.112  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 存储数据库成功!
    2020-04-13 19:26:37.113  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 解析到一条数据:{}
    2020-04-13 19:26:37.113  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 解析到一条数据:{}
    2020-04-13 19:26:37.113  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 解析到一条数据:{}
    2020-04-13 19:26:37.114  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 解析到一条数据:{}
    2020-04-13 19:26:37.114  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 解析到一条数据:{}
    2020-04-13 19:26:37.114  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 5条数据,开始存储数据库!
    2020-04-13 19:26:37.114  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 存储数据库成功!
    2020-04-13 19:26:37.114  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 解析到一条数据:{}
    2020-04-13 19:26:37.115  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 1条数据,开始存储数据库!
    2020-04-13 19:26:37.115  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 存储数据库成功!
    2020-04-13 19:26:37.115  INFO 17972 --- [           main] com.lh.easyexcel.util.DemoDataListener   : 所有数据解析完成!
    

    指定列的下标或者列名

        /**
         * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
         */
        @ExcelProperty(index = 2)
        private Double doubleData;
        /**
         * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
         */
        @ExcelProperty("字符串标题")
        private String string;
        @ExcelProperty("日期标题")
        private Date date;
    

    读多个sheet

            String fileName = "c://***";
            // 读取全部sheet
            // 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写
            EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();
            // 读取部分sheet
            fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
            ExcelReader excelReader = EasyExcel.read(fileName).build();
            // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener
            ReadSheet readSheet1 =
                EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
            ReadSheet readSheet2 =
                EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
            // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
            excelReader.read(readSheet1, readSheet2);
            // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
            excelReader.finish();
    

    日期、数字或者自定义格式转换

        /**
         * 自定义 转换器,不管数据库传过来什么 。给他加上“自定义:”
         */
        @ExcelProperty(converter = CustomStringStringConverter.class)
        private String string;
        /**
         * 这里用string 去接日期才能格式化。我想接收年月日格式
         */
        @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
        private String date;
        /**
         * 我想接收百分比的数字
         */
        @NumberFormat("#.##%")
        private String doubleData;
    
    public class CustomStringStringConverter implements Converter<String> {
        @Override
        public Class supportJavaTypeKey() {
            return String.class;
        }
        @Override
        public CellDataTypeEnum supportExcelTypeKey() {
            return CellDataTypeEnum.STRING;
        }
        /**
         * 这里读的时候会调用
         *
         * @param cellData
         *            NotNull
         * @param contentProperty
         *            Nullable
         * @param globalConfiguration
         *            NotNull
         * @return
         */
        @Override
        public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
            GlobalConfiguration globalConfiguration) {
            return "自定义:" + cellData.getStringValue();
        }
        /**
         * 这里是写的时候会调用 不用管
         *
         * @param value
         *            NotNull
         * @param contentProperty
         *            Nullable
         * @param globalConfiguration
         *            NotNull
         * @return
         */
        @Override
        public CellData convertToExcelData(String value, ExcelContentProperty contentProperty,
            GlobalConfiguration globalConfiguration) {
            return new CellData(value);
        }
    }
    

    不创建对象的读

    /**
     * 直接用map接收数据
     *
     * @author Jiaju Zhuang
     */
    public class NoModelDataListener extends AnalysisEventListener<Map<Integer, String>> {
        private static final Logger LOGGER = LoggerFactory.getLogger(NoModelDataListener.class);
        /**
         * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
         */
        private static final int BATCH_COUNT = 5;
        List<Map<Integer, String>> list = new ArrayList<Map<Integer, String>>();
    
        @Override
        public void invoke(Map<Integer, String> data, AnalysisContext context) {
            LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
            list.add(data);
            if (list.size() >= BATCH_COUNT) {
                saveData();
                list.clear();
            }
        }
    
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            saveData();
            LOGGER.info("所有数据解析完成!");
        }
    
        /**
         * 加上存储数据库
         */
        private void saveData() {
            LOGGER.info("{}条数据,开始存储数据库!", list.size());
            LOGGER.info("存储数据库成功!");
        }
    }
    
        /**
         * 不创建对象的读
         */
        @Test
        public void noModelRead() {
            String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
            // 这里 只要,然后读取第一个sheet 同步读取会自动finish
            EasyExcel.read(fileName, new NoModelDataListener()).sheet().doRead();
        }
    

    web中的读

        @PostMapping("upload")
        @ResponseBody
        public String upload(MultipartFile file) throws IOException {
            EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
            return "success";
        }
    
  • 相关阅读:
    tomcat日志切割脚本
    开源项目推荐:GSL科学计算函数库(GNU Scientific Library),实现VS2019源码编译
    MCUXpresso环境开发(本文争取做到您能够快速简易应用入门,知道大体流程,复杂功能和窗口设置一律不管)
    Arduino uno r3 使用 ESP8266 UARTWiFi 透传模块
    ESP8266引脚的说明
    windows10安装Trading View出错解决办法
    windows or linux 64位安装talib包
    python中list数组指定类型
    面试官问我JVM调优,我忍不住了!
    稳了!我准备了1个晚上的CMS垃圾收集器
  • 原文地址:https://www.cnblogs.com/luckyhui28/p/12693610.html
Copyright © 2020-2023  润新知