这段时期的工作涉及了不少报表类的开发,其中用到了主流的Excel处理工具EasyPoi和EasyExcel,下面是关于这两个工具用法小结。
一.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); }