zoukankan      html  css  js  c++  java
  • 告别硬编码,让你的POI导入导出拥抱变化

    已更新至V2.0.0,前往查看

    GitHub | 博客 | 中文 | English | 原文链接

    为什么使用AutoExcel?

    Excel导入导出在软件开发中非常常见,只要你接触过开发,就一定会遇到。相信很多人会跟我一样选择用Apache POI来完成这项工作,在感受到POI功能强大的同时,我的团队也遇到了以下问题:

    1. 直接使用POI操作Excel将产生大量硬编码,你会在编码中写死行索引和列索引
    2. 大量不可复用的格式控制编码,如背景色、对齐方式、单元格样式等
    3. 实施顾问明明提供了现成的模板,却还要开发用代码实现一遍,开发效率低下
    4. 模板调整时不得不动用开发资源
    5. 简单的导出也需要写特定的代码

    AutoExcel解决了上述问题,它非常简单,只需要少量的代码即可完成复杂的导入导出;使用它时,程序员对导入导出无感,即不需要直接操作POI;与此同时,实施顾问提供的Excel即是导入导出模板,除非新增数据源或字段,否则模板更新不需要动用开发资源。

    AutoExcel并没有对POI进行过重的封装,而是充分利用了Excel本身具有的特性——名称管理器,通过一些小技巧,将单元格与数据源产生映射,从而解耦程序员与POI,避免产生硬编码,让导入导出工作变得愉快而不再是枯燥乏味。

    功能预览

    导出前 导出后
    image image
    image image
    image image
    image image

    实现以上导出只需要编写以下少量代码(你需要额外的代码来准备数据源,例如从数据库中获取)

    List<TemplateExportPara> paras = new ArrayList<>();
    paras.add(new TemplateExportPara("BusinessUnit", DataGenerator.genBusinessUnit()));
    paras.add(new TemplateExportPara("Contract", DataGenerator.genContracts()));
    paras.add(new TemplateExportPara("Project", DataGenerator.genProjects()));
    
    List<Product> products = DataGenerator.genProducts();
    TemplateExportPara para3 = new TemplateExportPara("Product", products);
    para3.setInserted(true);
    paras.add(para3);
    
    TemplateExportPara para5 = new TemplateExportPara("Product2", products);
    para5.setDataDirection(DataDirection.Right);
    paras.add(para5);
    
    ExcelSetting excelSetting = new ExcelSetting();
    excelSetting.setRemovedSheets(Arrays.asList("will be removed"));
    
    AutoExcel.save(this.getClass().getResource("/template/Common.xlsx").getPath(),
                   this.getClass().getResource("/").getPath() + "ExportWithTemplate.xlsx",
                   paras,
                   excelSetting);
    

    认识模板

    要实现以上导出,首先需要完成模板的制作。一些报表制作工具如微软的RDL,你会在RDL中制作好导出模型,然后结合代码将数据导出到Excel。这个过程,RDL仅仅起到中介作用,意味着每次有新的导出任务,都得先制作一个导出模型。在AutoExcel中,Excel即模板,如果你的Excel来源是实施顾问,很可能这个Excel已经设定好数据格式、单元格样式等,就等着你往上填数据,既然如此,何不就将这份Excel作为我们的导出模板,我们要做的,仅仅是在其中加入我们的东西而已。

    名称管理器

    Excel中的名称管理器,这个被大多数人忽视的功能,却成为AutoExcel中连接数据源与单元格的桥梁。你可以通过点击菜单公式->名称管理器来打开名称管理器,其中每一个名称都对应Excel中的某个具体位置,可以是一个区域,也可以是一个单元格,当然,在这里,我们定义的名称都指向单元格。因此可以这么理解,名称管理器就是用来给单元格命名的。正是因为单元格有了名字,我们才能实现给单元格自动赋值而无需个性化的代码。

    image

    为单元格定义了名称之后,当你再次点击该单元格,会发现左上角的位置显示了你刚才定义的名称

    image

    除了在名称管理器中新增名称,还有一种方式更加直观快捷。点击你想要命名的单元格,然后直接在左上角输入名称,最后按Entry键即可。推荐使用这种方式创建名称。

    image

    名称规则

    由于单元格名称决定了何种数据按什么方式进行填充,因此必须按以下规则进行命名:

    1. 数据源名称.字段名称[.合计类型],用于填充普通字段或普通字段的合计值,如:product.SaleArea.sum
    2. 数据源名称.Formula.xxxx,用于填充公式,如:product.Formula.1
    3. 数据源名称.RowNo,用于填充行号,如:product.RowNo

    所有名称均不区分大小写,以下会根据具体场景分别进行介绍

    导出

    基础对象

    image

    如图所示,批注中注明了每个单元格的名称,按照数据源名称.字段名称的规则书写

    Java代码:

    String templatePath = this.getClass().getResource("/template/Common.xlsx").getPath();
    String outputPath = this.getClass().getResource("/").getPath() + "ExportWithTemplate.xlsx";
    //DataGenerator.genBusinessUnit()用于生成demo数据
    TemplateExportPara para = new TemplateExportPara("BusinessUnit", DataGenerator.genBusinessUnit());
    AutoExcel.save(templatePath, outputPath, para);
    

    单表

    image

    如果你想导出的是一个列表数据,你只需要按照基础对象的书写规则进行命名即可。当然,列表数据导出往往比基础对象复杂,比如,你可能需要一列行号,而又不想在代码中做特殊处理,这时候你可以使用数据源名称.RowNo,将工作交给AutoExcel来处理。注意,RowNo是内置字段,如果数据源中包含此字段,将会被覆盖。

    还有一种情况非常常见,你有一个带公式的单元格在表格中,如:=E6+F6,你希望下一行的单元格被赋值=E7+F7,这时你应该使用数据源名称.Formula.xxxx,你可以使用任何你喜欢的公式,AutoExcel最终都会帮你自动填充。xxxx的部分你可以随便书写,只要保证名称唯一即可。Formula同样是内置字段。

    Java代码:

    String templatePath = this.getClass().getResource("/template/Common.xlsx").getPath();
    String outputPath = this.getClass().getResource("/").getPath() + "ExportWithTemplate.xlsx";
    //DataGenerator.genContracts()用于生成demo数据
    TemplateExportPara para = new TemplateExportPara("Contract", DataGenerator.genContracts());
    AutoExcel.save(templatePath, outputPath, para);
    

    多表

    image

    在一个Sheet中导出多个表格,如果你有这样的需求,请在代码中将不是处于最下面的表格的导出参数设置为:setInserted(true),如上图,products对应的导出参数para应做如下设置:para.setInserted(true)。要知道,AutoExcel是不理会数据导出是否有足够空间的,它只会一个劲地输出,所以当你的模板空间不够时,你需要告诉AutoExcel,之后AutoExcel会在导出前腾出足够的空间来容纳你的数据。

    这里引入了新的命名规则:数据源名称.字段名称.合计类型,用于对指定字段进行合计,目前支持两种合计类型:Sum和Avg。

    Java代码:

    String templatePath = this.getClass().getResource("/template/Common.xlsx").getPath();
    String outputPath = this.getClass().getResource("/").getPath() + "ExportWithTemplate.xlsx";
    List<TemplateExportPara> paras = new ArrayList<>();
    //DataGenerator.genProjects()用于生成demo数据
    paras.add(new TemplateExportPara("Project", DataGenerator.genProjects()));
    
    //DataGenerator.genProducts()用于生成demo数据
    TemplateExportPara para = new TemplateExportPara("Product", DataGenerator.genProducts());
    para.setInserted(true);  //导出空间不够时需设置
    paras.add(para);
    
    AutoExcel.save(templatePath, outputPath, paras);
    

    向右填充

    image

    如果你需要将数据向右而不是向下填充,你只需要使用setDataDirection(DataDirection.Right)即可。

    Java代码:

    String templatePath = this.getClass().getResource("/template/Common.xlsx").getPath();
    String outputPath = this.getClass().getResource("/").getPath() + "ExportWithTemplate.xlsx";
    TemplateExportPara para = new TemplateExportPara("Product2", DataGenerator.genProducts());
    para.setDataDirection(DataDirection.Right);  //向右填充
    AutoExcel.save(templatePath, outputPath, para);
    

    直接导出

    直接导出,即导出过程不需要借助模板,适用于集成到后台系统的通用导出功能中,代码非常简单

    String outputPath = this.getClass().getResource("/").getPath() + "Export.xlsx";
    DirectExportPara para = new DirectExportPara(DataGenerator.genProjects());
    AutoExcel.save(outputPath, para);
    

    效果:

    image

    当然,你并不会喜欢这样的标题和标题顺序,因此,你需要借助FieldSetting来让你的标题可读且按照你喜欢的顺序来展现。

    List<FieldSetting> fieldSettings = new ArrayList<FieldSetting>() {{
        add(new FieldSetting("projName", "Project Name"));
        add(new FieldSetting("basalArea", "Basal Area"));
        add(new FieldSetting("buildingArea", "Building Area"));
        add(new FieldSetting("insideArea", "Inside Area"));
        add(new FieldSetting("availableArea", "Available Area"));
        add(new FieldSetting("availablePrice", "Available Price"));
        add(new FieldSetting("availableAmount", "Available Amount"));
    }};
    String outputPath = this.getClass().getResource("/").getPath() + "Export.xlsx";
    DirectExportPara para = new DirectExportPara(DataGenerator.genProjects(), "Projects", fieldSettings);
    AutoExcel.save(outputPath, para);
    

    最终效果:

    image

    自定义操作

    AutoExcel致力于处理导入导出的通用场景,如果有个性化的需求,你应该取回Workbook的控制权,根据你的需求进行个性化处理。save方法提供了两个Consumer,其中actionAhead会在导出操作开始之前被调用,actionBehind会在导出完成之后被调用,你完全可以通过这两个Consumer来添加你想要的功能。

    String templatePath = this.getClass().getResource("/template/Common.xlsx").getPath();
    String outputPath = this.getClass().getResource("/").getPath() + "ExportWithTemplate.xlsx";
    List<TemplateExportPara> paras = new ArrayList<>();
    paras.add(new TemplateExportPara("BusinessUnit", DataGenerator.genBusinessUnit()));
    Consumer<Workbook> actionAhead = Workbook -> {
        //做任何你想做的事情
    };        
    Consumer<Workbook> actionBehind = workbook -> {
        //做任何你想做的事情
    };
    AutoExcel.save(templatePath, outputPath, paras, actionAhead, actionBehind);
    

    导入

    相较于导出,导入具有以下特点:

    1. 仅支持一种名称规则:数据源名称.字段名称
    2. 暂不支持一个Sheet有多个表的情况
    3. 默认数据读取方向(DataDirection)为null,即读取基础对象,如需读取列表,需指明读取方向为Down,暂不支持Right方向的读取。

    Java代码:

    List<ImportPara> importParas = new ArrayList<ImportPara>() {{
        add(new ImportPara("BusinessUnit"));
        add(new ImportPara("Contract", DataDirection.Down));
        add(new ImportPara("Project", DataDirection.Down));
        //add(new ImportPara("Product", DataDirection.Down));   暂不支持
    }};
    String fileName = this.getClass().getResource("/").getPath() + "ExportWithTemplate.xlsx";
    HashMap<String, List<HashMap<String, Object>>> datas = AutoExcel.read(fileName, importParas);
    

    运行示例代码

    完整的示例代码请前往单元测试查看

    GitHub地址

    image

  • 相关阅读:
    topcoder srm 320 div1
    topcoder srm 325 div1
    topcoder srm 330 div1
    topcoder srm 335 div1
    topcoder srm 340 div1
    topcoder srm 300 div1
    topcoder srm 305 div1
    topcoder srm 310 div1
    topcoder srm 315 div1
    如何统计iOS产品不同渠道的下载量?
  • 原文地址:https://www.cnblogs.com/fenghaitao/p/autoexcel-user-manual.html
Copyright © 2011-2022 走看看