zoukankan      html  css  js  c++  java
  • 2021.05.08 easyExcel简单读写

    前言

    web开发中,我们经常会遇到数据导入或者导出excel的需求,如果用原生的poi(阿帕奇的顶级项目,项目地址:https://poi.apache.org/),虽然能够满足我们的需求,但是原生的接口用起来很不方便,数据格式都需要我们设定,而且它又是基于单元格的操作,在多个数据导出导入的场景下,每个场景都需要定制化的导出入方法,很不方便。

    那有没有一个组件,能够根据我们的Vo导出excel,导出的时候,每行一条数据,传入一个lsit就可以实现所有数据的导出;导入的时候,指定vo,然后就可以把excel转成一个volist,方便我们的操作?

    easyExcel是什么

    有呀,当然有呀,easyExcel就是这样的一个组件,它是由阿里巴巴开发的一个基于Java的简单、省内存的读写Excel的开源组件,在尽可能节约内存的情况下支持读写百MExcel

    github地址:https://github.com/alibaba/easyexcel

    根据官方文档,该组件有着较好的性能:

    接下来,我们就来看下如何在我们自己的项目中快速地使用它。

    简单应用

    添加依赖

    首先需要先引入easyExcel的依赖,目前最新的发布版本是2.2.6

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.2.6</version>
    </dependency>
    

    创建数据对应的VO

    这里用到了lombox,所有就不需要自己创建gettersetter方法,如果你不想用lombox,可以手动创建getter/setter方法。

    import com.alibaba.excel.annotation.ExcelProperty;
    import lombok.Data;
    
    /**
     * @program: example-2021.05.08
     * @description: demoData
     * @author: syske
     * @date: 2021-05-08 18:12
     */
    @Data
    public class DemoData {
        @ExcelProperty("名称")
        private String name;
    
        @ExcelProperty("标题")
        private String title;
    
        @ExcelProperty("描述")
        private String description;
    }
    

    核心的点就一个,@ExcelProperty其实就是指定数据的表头,当然也可以通过注解的方式,直接指定表头样式

    简单写excel

     public static String writeExcel() {
            String fileName = UUID.randomUUID().toString() + ".xlsx";
         	// 创建ExcelWriterBuilder
            ExcelWriterBuilder write = EasyExcel.write(fileName, DemoData.class);
            // 创建sheet
            ExcelWriterSheetBuilder sheet = write.sheet("模板");
           // 构建数据
            DemoData demoData = new DemoData();
            demoData.setDescription("描述信息");
            demoData.setName("名称");
            demoData.setTitle("标题");
            List<DemoData> dataList = Lists.newArrayList(demoData);
            // 写入数据
            sheet.doWrite(dataList);
            return fileName;
        }
    

    注释已经够详细了,这里再简单解释下,我们先要创建ExcelWriterBuilder,这里要指定生成的excel的完整文件名(包括保存路径)和excel对应的VO;

    然后再创建sheet,如果经常用excel的小伙伴应该知道,一个excel可以创建多个sheet,而且sheet的名字必须唯一,这里我们指定的sheet名称是“模板”;

    再接着,我们要创建我们要写入的数据,也就是前面VO对应的List

    最后,将List的数据写入。

    跑一下上面的代码,会生成这样的excel

    我们并没有指定表头,但是发现生成的excel是有表头的,而且就是我们前面创建VO指定的ExcelProperty,这也印证了我们前面的说法,当然它也支持自定义表头,我们稍后再看

    简单读excel

    自定义解析事件监听

    相比写,读稍微复杂一点,需要我们自定义一个EventListener,继承AnalysisEventListener就可以了:

    public class DemoDataListener extends AnalysisEventListener<DemoData> {
        private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
        /**
         * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
         */
        private static final int BATCH_COUNT = 5;
        List<DemoData> list = Lists.newArrayList();
    
        public DemoDataListener() {}
    
    
        /**
         * 这个每一条数据解析都会来调用
         *
         * @param data
         *            one row value. Is is same as {@link AnalysisContext#readRowHolder()}
         * @param context
         */
        @Override
        public void invoke(DemoData data, AnalysisContext context) {
            LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
            System.out.println("解析数据:" + JSON.toJSONString(data));
            list.add(data);
            // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
            if (list.size() >= BATCH_COUNT) {
                saveData();
                // 存储完成清理 list
                list.clear();
            }
        }
    
        /**
         * 所有数据解析完成了 都会来调用
         *
         * @param context
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            // 这里也要保存数据,确保最后遗留的数据也存储到数据库
            saveData();
            LOGGER.info("所有数据解析完成!");
        }
    
        /**
         * 加上存储数据库
         */
        private void saveData() {
            LOGGER.info("{}条数据,开始存储数据库!", list.size());
            LOGGER.info("存储数据库成功!");
        }
    }
    

    这里其实就相当于excel的解析器,我们只需要重写相应的方法即可。 invoke(DemoData data, AnalysisContext context)方法是每解析一行数据,就会被调用,在这里你可以做数据的校验和转换操作;doAfterAllAnalysed(AnalysisContext context)是所有数据解析完成后进行的操作,类似回调函数,你可以根据自己的业务需求进行修改。

    开始读

    然后读取的话,就很简单了:

    public static void readExcel(String filePath) {
            EasyExcel.read(filePath, DemoData.class, new DemoDataListener()).sheet().doRead();
        }
    

    默认情况下,数据是从第二行开始读取的,第一行默认为表头,当然你也可以通过headRowNumber(Integer headRowNumber)指定从第几行开始读取:

    EasyExcel.read(filePath, DemoData.class, new DemoDataListener()).sheet().headRowNumber(0).doRead();
    

    另外,有一点需要注意,就是读取的时候,excel的字段顺序要和VO的字段顺序保持一致,否则会出现串行的问题,当然你也可以通过指定字段顺序的方式解决这个问题,索引顺序从0开始,当然这个index也会影响导出的excel里面的字段顺序。

    设定写的样式

    设定样式有两种方式,一种是通过代码的方式设定,另一种就是通过注解的方式实现,我们先看第一种方式。

    代码设定样式
    public static String writeExcel() {
            String fileName = UUID.randomUUID().toString() + ".xlsx";
            // 头的策略
            WriteCellStyle headWriteCellStyle = new WriteCellStyle();
            // 背景设置为红色
            headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
            WriteFont headWriteFont = new WriteFont();
            headWriteFont.setFontHeightInPoints((short) 20);
            headWriteCellStyle.setWriteFont(headWriteFont);
            // 设定bord
            headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
            headWriteCellStyle.setBorderTop(BorderStyle.THIN);
            headWriteCellStyle.setBorderRight(BorderStyle.THIN);
            headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
            // 内容的策略
            WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
            // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
            contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
            // 背景绿色
            contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());
             // 设定边框样式
            contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
            contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
            contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
            contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
            WriteFont contentWriteFont = new WriteFont();
            // 字体大小
            contentWriteFont.setFontHeightInPoints((short) 20);
            contentWriteCellStyle.setWriteFont(contentWriteFont);
            // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
            HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                    new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    		// 设定样式
            ExcelWriterBuilder write = EasyExcel.write(fileName, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy);
            ExcelWriterSheetBuilder sheet = write.sheet("模板");
            DemoData demoData = new DemoData();
            demoData.setDescription("描述信息");
            demoData.setName("名称");
            demoData.setTitle("标题");
            List<DemoData> dataList = Lists.newArrayList(demoData);
            sheet.doWrite(dataList);
            return fileName;
        }
    

    代码和上面写的代码一样,只是增加了样式的设定,就是在创建ExcelWriterBuilder的时候,指定样式registerWriteHandler(horizontalCellStyleStrategy)

    setFillForegroundColor设定背景颜色,也就是填充颜色;

    setFontHeightInPoints设定字体大小;

    setWriteFont是设定字体样式;

    setBorder是设定边框样式;

    然后我们再来看下生成的效果:

    但是我发现,这里的样式没法指定列宽,下面我们看下注解的样式是不是可以指定。

    注解设定样式

    首先我们看下列宽度如何设定,设置方式很简单,只需要在对应的字段上加上ColumnWidth注解即可:

    之后的效果如下

    然后,我们在通过注解设定其他样式,我在名称上面添加如下注解:

    然后生成的效果变成这样了:

    代码里面的样式设定也没有删除,说明启用注解之后,代码里面设定的样式就失效了。其中,HeadStyle是设定标题栏样式,HeadFontStyle是设定标题字体字体样式,ContentStyle是设定内容的样式,ContentFontStyle是设置内容的字体样式,样式的内容可以根据自己的需求进行调整,这里就不作过多的说明了。

    总结

    easyExcel对于列表式的数据导入导出,是特别友好的,而且用起来也特别方便,但是对于不规律的表格(比如票据)poi可能是更好的选择,当然easyExcel本身也是基于poi实现的,只是前者对用户更友好。

    简单总结下,今天我们通过几个简单的示例,演示了如何用easyExcel实现数据的导入导出,展示了设置样式的两种方式,可以说上面的这些内容基本上可以满足我们大部分的应用场景,但如果你还有很多其他的业务需求,建议你再去研究下官方文档,更高级的用法还有很多,今天我们算是抛砖引玉了,有兴趣的小伙伴自己去看吧!

    好了,今天是母亲节,让我们一起祝所有的母亲节日快乐,当然更重要的是别忘了给自己的妈妈打个电话,给一句问候,一句关心……

    项目路径:

    https://github.com/Syske/example-everyday
    

    本项目会每日更新,让我们一起学习,一起进步,遇见更好的自己,加油呀

  • 相关阅读:
    xml在html中的使用
    getElementsByTagName的用法
    opener 的使用
    动态增加属性
    innerText, innerHTML,outerHTML
    button的css属性
    动态改变select中option的次序
    input 的样式
    zkw线段树
    接着吐槽(2012/5/23)
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/14747984.html
Copyright © 2011-2022 走看看