Excaliburer`s Zone

It was challenging, but not risky.

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

  这段时期的工作涉及了不少报表类的开发,其中用到了主流的Excel处理工具EasyPoiEasyExcel,下面是关于这两个工具用法小结。

一.Excel的填充(导出的一种)

  关于Excel的导出主要分为两种形式,一种是事先给出一个确定的excel文件作为导出模板,然后将生成的数据写入到这个Excel文件中(即excel模板的填充);另一种就是不需要事先给定好excel文件,而是在生成数据的过程中动态的生成导出模板的格式,相当于把数据和模板样式一起动态的生成。

  首先是实体类(实体类不用加单独的注解,就是普通的业务实体类):

public class tcExcelDTO {
    /**
     *  ID
     */
    private Long orderNum;
    /**
     *  单位编号
     */
    private String deptCode;
    /**
     *  单位名称
     */
    private String deptName;
    /**
     *  管理类数量
     */
    private Integer manageNum;
    /**
     *  管理类分值
     */
    private BigDecimal manageScore;
    /**
     *  技术质量类数量
     */
    private Integer technicalQualityNum;
    /**
     *  技术质量类分值
     */
    private BigDecimal technicalQualityScore;
    /**
     *  生产类数量
     */

    private Integer productionNum;
    /**
     *  生产类分值
     */
    private BigDecimal productionScore;
}

  

  1.EasyPoi的填充

  (1)Excel模板设置格式:参考EasyPoi教程_V1.0 (mydoc.io),具体位置如下:

 

  

 

     本文使用的例子如下

      

  (2)参考实现代码:

public void applyResyltExport(HttpServletResponse response, HrIsProjectApplysBusiBo hrIsProjectApplysBusiBo)
        throws Exception{
    List<tcExcelDTO> exportTemplateListJiTuan = buildJiTuanData(hrIsProjectApplysBusiBo);//sheet1中的数据
    List<tcExcelDTO> exportTemplateListCompany = buildCompanyData(hrIsProjectApplysBusiBo);//sheet2中的数据
    String fileName = hrIsProjectApplysBusiBo.getYear() +"年"+
            hrIsProjectApplysBusiBo.getQuarter() + "季度" + EXCEL_NAME;
    TemplateExportParams params=new TemplateExportParams("templates/import/finalApplyResult.xlsx",true);//需要填充的模板的路径
    Map<String, Object> map = new HashMap<>();

    map.put("list",exportTemplateListCompany);//sheet1,list和模板中的list名称相对应
    map.put("list2",exportTemplateListJiTuan);//sheet2,list1和模板中的list1名称相对应
    Workbook workbook = ExcelExportUtil.exportExcel(params,map);//easypoi的依赖
    try {
        response.setHeader("Content-disposition", "attachment;filename=" + new String(fileName.getBytes("utf-8"),"ISO-8859-1" )+".xlsx");
        workbook.write(response.getOutputStream());
        response.getOutputStream().flush();
    } catch (IOException e) {
        log.error("导出失败,请检查", e);
        throw new BaseException(ErrorEnum.SYSTEM_ERROR, "导出失败,请检查");
    } finally {
        try {
            response.getOutputStream().close();
        } catch (IOException e) {
            log.error("导出失败,请检查", e);
            throw new BaseException(ErrorEnum.SYSTEM_ERROR, "导出失败,请检查");
        }
    }

}

  

   此处主要使用了语法:{{fe:list t.age t.secondList.name}},需要注意的是使用指令fe 循坏数据,如果使用$fe 嵌套列表第一个字段为空时,第二行后面的数据显示不出来了。

  2.EasyExcel的填充

  (1)Excel模板设置格式:参考填充Excel | Easy Excel (alibaba.com),由于官方例子讲述的很详细,此处不在赘述。

二.Excel的导出(动态生成模板)

  1.EasyPoi的导出

  (1)首先设置相应的实体类:

public class testExcelDTO {
    /**
     *  ID
     */
    private Long id;
    /**
     *  项目编号
     */
    @Excel(name = "项目编号", orderNum = "1", width = 25)
    private String projectCode;
    /**
     *  项目名称
     */
    @Excel(name = "项目名称", orderNum = "2", width = 25)
    private String projectName;
    /**
     *  主持人工号
     */
    @Excel(name = "工号", orderNum = "3", width = 25)
    private String empNo;
    /**
     *  主持人姓名
     */
    @Excel(name = "姓名", orderNum = "4", width = 25)
    private String empName;
    /**
     *  角色
     */
    @Excel(name = "角色", replace = { "主持人_100001", "主要参与人_100002", "一般参与人_100003", "辅助参与人_100004" },orderNum = "5", width = 25)
    private Integer role;
    /**
     *  年度
     */
    private String year;
    /**
     *  季度
     */
    private String quarter;
    /**
     *  项目来源
     */
    private String source;
public static HrIsProjectPitemExcelDTO generateBean() {
    return new HrIsProjectPitemExcelDTO();
}
}

  (2)代码实现:

 public void testExport(HttpServletResponse response, HrIsProjectPitemBusiBo hrIsProjectPitemBusiBo)
            throws Exception{
        // 设置导出数据
        List<testExcelDTO> exportTemplateList = buildEmportTemplateData(hrIsProjectPitemBusiBo);//生成需要导出的数据
        log.info("exportTemplateList : {}", exportTemplateList);
        if (!org.apache.commons.collections4.CollectionUtils.isEmpty(exportTemplateList)) {
            List<Map<String, Object>> excelParamList = new ArrayList<>();
            excelParamList.add(ExportExcelUtil.getExcelSheetParam(SHEET_NAME ,
                    exportTemplateList, testExcelDTO.class));
            StringBuffer fileName = new StringBuffer(hrIsProjectPitemBusiBo.getYear() +"年"+
                    hrIsProjectPitemBusiBo.getQuarter() + "季度" + EXCEL_NAME);
            fileName.append(".xlsx");
            ExportExcelUtil.exportExcel(excelParamList, ExcelType.XSSF, fileName.toString(), response);
        } else {
            throw new BaseException(ErrorEnum.SYSTEM_ERROR, "导出数据为空!");
        }

  其中ExportExcel的定义如下:

public class ExportExcelUtil {
  
    /**
     * 导出到workbook并输出到response
     * @param list   数据集
     * @param excelType  文件类型ExcelType.HSSF/XSSF
     * @return
     * @throws
     */
    public static void exportExcel(List<Map<String, Object>> list, ExcelType excelType, String fileName, HttpServletResponse response) throws IOException {
        try {
            Workbook workbook = ExcelExportUtil.exportExcel(list, excelType);
            if (workbook != null) {
                response.setHeader("content-Type", "application/vnd.ms-excel");
                response.setHeader("Content-disposition", "attachment;filename=" + new String(fileName.getBytes("utf-8"),"iso-8859-1" ));
                workbook.write(response.getOutputStream());
                response.getOutputStream().flush();
                // 关闭流
                response.getOutputStream().close();
            }
        }catch (IOException e){
            throw new IOException("导出表格数据失败");
        }
    }

    // 流导出
    private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {
        try {
            response.reset();
            response.setContentType("application/x-download");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes("utf-8"), "ISO-8859-1"));
            workbook.write(response.getOutputStream());
        }catch (IOException e){
            throw new IOException("导出表格数据失败");
        }
    }

    // 生成预选值的sheet页
    private static XSSFDataValidationHelper createXSSFDataValidationHelper(Workbook workbook, String name, String[] strList) {
        Sheet sheet = workbook.createSheet(name);
        // 循环往该sheet中设置添加下拉列表的值
        for (int i = 0; i < strList.length; i++) {
            Row row = sheet.createRow(i);
            Cell cell = row.createCell((int) 0);
            cell.setCellValue(strList[i]);
        }
        workbook.setSheetHidden(workbook.getSheetIndex(name), true);//隐藏用于生成下拉框的sheet
        XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet) workbook.getSheet(name));
        dvHelper.createFormulaListConstraint(name + "!$A$1:$A$" + strList.length);
        return dvHelper;
    }
    /**
     * 生成含有下拉框的模板
     * firstRow 开始行号(默认为1,下标0开始)
     * lastRow  根据此项目,默认为最大65535
     * firstCol 区域中第一个单元格的列号 (下标0开始)
     * lastCol 区域中最后一个单元格的列号
     * strings 下拉内容
     * */
    public static void exportContainDownVlue(Workbook workbook, HttpServletResponse response,String mySheetNmme,String fileName,String[]dropDownValue,
    int firstCol,int lastCol ) throws IOException {

        String  dropDownSheetName= "隐藏sheet";
        XSSFDataValidationHelper dropDownValidationHelper = createXSSFDataValidationHelper(workbook, dropDownSheetName, dropDownValue);
        DataValidationConstraint dropDOwnValidationConstraint = dropDownValidationHelper.createFormulaListConstraint(dropDownSheetName + "!$A$1:$A$" + dropDownValue.length);
        Sheet firstSheet = workbook.getSheet(mySheetNmme);
        CellRangeAddressList drowDownValueCoveringRowsAndCloumns = new CellRangeAddressList(1, firstSheet.getLastRowNum(), firstCol, lastCol);
        XSSFDataValidation dropDownValidation =(XSSFDataValidation)dropDownValidationHelper.createValidation(dropDOwnValidationConstraint, drowDownValueCoveringRowsAndCloumns);
        firstSheet.addValidationData(dropDownValidation);
        downLoadExcel(fileName, response, workbook);
    }

}

 (3)导出结果示例:

 

 

 2.EasyExcel的导出

  官方文档的Demo写的很清晰,此处不在赘述写Excel | Easy Excel (alibaba.com)

 

三.Excel的导入

  1.EasyPoi

  (1)实体类如下:

public class testExcelDTO {
    /**
     *  ID
     */
    private Long id;
    /**
     *  项目编号
     */
    @Excel(name = "项目编号", orderNum = "1", width = 25)
    private String projectCode;
    /**
     *  项目名称
     */
    @Excel(name = "项目名称", orderNum = "2", width = 25)
    private String projectName;
    /**
     *  主持人工号
     */
    @Excel(name = "工号", orderNum = "3", width = 25)
    private String empNo;
    /**
     *  主持人姓名
     */
    @Excel(name = "姓名", orderNum = "4", width = 25)
    private String empName;
    /**
     *  角色
     */
    @Excel(name = "角色", replace = { "主持人_100001", "主要参与人_100002", "一般参与人_100003", "辅助参与人_100004" },orderNum = "5", width = 25)
    private Integer role;
    /**
     *  年度
     */
    private String year;
    /**
     *  季度
     */
    private String quarter;
    /**
     *  项目来源
     */
    private String source;

}

  这个实体类和导入的时候类似的,需要通过注解@Excel对应好数据的映射关系

 (2)代码实现如下:

public DataImportResultBO testExcelImport(@NotNull @RequestParam("file") MultipartFile file,
                                                         @RequestParam(value = "id") @Number(min=1,message = "ID必须大于0")  Long id) throws Exception {
        String intekeyUrl = NacosGlobalConfiguration.getByKey("intekey-url");
        String appCode = NacosGlobalConfiguration.getByKey("intekey-appCode");
        String secretKey = NacosGlobalConfiguration.getByKey("intekey-secretKey");
        InputStream inputStream = IntekeyUtils.DecryptFile(intekeyUrl,appCode,secretKey,file);//由于导入的wxcel文件是加密的,此处需要解密
        ImportParams importParams = new ImportParams();
//        importParams.setKeyIndex(0);
        importParams.setNeedVerify(true);
        ExcelImportResult<testExcelDTO> excelImportResult = ExcelImportUtil
                .importExcelMore(inputStream, testExcelDTO.class, importParams);//easypoi提供的接口
        List<testExcelDTO> list = excelImportResult.getList();
        DataImportResultBO resultBO = new DataImportResultBO();
        List<testExcelDTO> failList = excelImportResult.getFailList();
        if (failList != null && failList.size() > 0) {//打印错误信息
            int count = failList.size();
            StringBuilder stringBuilder = new StringBuilder();
            for (testExcelDTO dto : failList) {
                stringBuilder
                        .append("第")
                        .append(dto.getRowNum())
                        .append("行,")
                        .append(dto.getErrorMsg())
                        .append(";");
            }
            resultBO.setMsg(stringBuilder.toString());
            resultBO.setErrorNumber(count);
            resultBO.setIsSuccess(false);
        } else {
            resultBO.setErrorNumber(0);
        }
        if(list.size()==0 && CollectionUtils.isEmpty(failList)){//对应解析excel失败的处理
            final boolean[] isSuccess = {true};
            isSuccess[0] = false;
            resultBO.setIsSuccess(isSuccess[0]);
            resultBO.setSuccessNumber(0);
            resultBO.setMsg("批量导入excel未解析到数据");
            log.error("批量导入excel未解析到数据!");
            return resultBO;
        }else {//业务逻辑处理 
            int successNum = 0;
            for (testExcelDTO hrIsCashSpecialApplyExcelBusiDTO : list) {
                testExcelDTO hrIsCashSpecialApplyBusiDTO = new HrIsCashSpecialApplyBusiDTO();
                XXXXXXXX //业务逻辑
                successNum++;
            }
            resultBO.setSuccessNumber(successNum);
            resultBO.setErrorNumber(list.size() - successNum);
            resultBO.setIsSuccess(true);
            return resultBO;
        }
    }

  其中返回的信息类定义如下:

public class DataImportResultBO<E> implements Serializable {

    /**
     * 本次导入编码
     */
    private String importCode;

    /**
     * 导入成功条数
     */
    private Integer successNumber;

    /**
     * 失败条数
     */
    private Integer errorNumber;

    /**
     * 导入结果
     */
    private Boolean isSuccess;

    /**
     * 错误信息
     */
    private String msg;

    /**
     * 导入数据
     */
    List<E> result;
}

  2.EasyExcel

  参考官方文档:读Excel | Easy Excel (alibaba.com)

四.其他

  1.EasyPoi导入、导出时不支持三级以上表头,但是也可以实现,只是相对复杂些,关于EasyPoi的多级表头的导入、导出可以参考:

  (1) (35条消息) excel导入基于Easypoi一对多导入(实现合并单元格)_秃头老程序员的博客-CSDN博客_easypoi导入excel一对多

  (2) (35条消息) 使用easypoi或者easyexcel多表头导出_山里的小蝌蚪的博客-CSDN博客_easypoi 导出表头

  (3) easyExcel简单excel导出以及多sheet页导出 - 简书 (jianshu.com)

  2.关于带下拉框的Excel的导出

  参考代码:

 /**
     * 自定义excel下拉框内容
     * @param workbook
     * @param firstCol 需要设置下拉框的开始行
     * @param lastCol 需要设置下拉框的开始列
     * @param strings,下拉框选项
     */
    private static void selectList(Workbook workbook,int firstCol,int lastCol,String[] strings) {
        Sheet sheet = workbook.getSheetAt(0);
        //  生成下拉列表
        //  只对(x,x)单元格有效
        CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 65535, firstCol, lastCol);
        //  生成下拉框内容
        DVConstraint dvConstraint = DVConstraint.createExplicitListConstraint(strings);
        HSSFDataValidation dataValidation = new HSSFDataValidation(cellRangeAddressList, dvConstraint);
        //  对sheet页生效
        sheet.addValidationData(dataValidation);
    }

  

posted on 2022-07-06 09:59  Excaliburer  阅读(372)  评论(0编辑  收藏  举报