最新在使用easypoi,使用注解导出和模板导出的方式,现在主要就模板导出合并单元格一些坑和解决方法。
首先我建议刚接触的同学看这篇文章,很详细,功能也比较全面,比较感谢这篇文章的原创作者,网站1:easyPOI基本用法 - 钟小嘿 - 博客园
然后模板导出一些坑,可以看这篇文章,作者写的很详细,目前模板导出存在的一些问题,网站2:springboot集成easypoi并使用其模板导出功能和遇到的坑_巴中第一皇子的博客-CSDN博客_springboot的poi
这几天因为最后一个问题捣鼓了好久,最后写了一个临时方案来处理:
我目前需要处理的报表样式如下:
上面是一个excel报表,上下分成两部分,分别是两个list(easypoi模板可以允许多list的)
这里的坑,可以看网站2中第四条中的说明,两种方法,即一个使用$fe , 一个使用fe (汗,第一次使用这个,我都没发现这两个不一样), 但是都存在缺陷,
这种多层循环,使用$fe会产生空白行, 使用fe会覆盖循环的数据以下的内容, 如我需求里有 “合计” 行,必定会覆盖。
尝试了很多次后,我的想法是,先把 以人员为维度的所有数据全部查出来,比如上面这个excel表,我查询的数据总共就是5条(这里分成两个list,上面一个3条,下面是2条),这时可能有人疑惑,但是“公司”和“项目” 我是要相同的合并在一起呀,不错,我先以“人员”为粒度查出所有,然后导出前对需要合并的列进行合并。
首先这里两个list都必须使用$fe,因为这样才不会有覆盖的情况产生。
下面是我的模板,分成zkr和bat两个map,里面各自有对应的list:
下面是我封装了下模板导出兼容合并单元格的方法,先贴网站1原始方法:
/*** 根据模板生成excel后导出** @param templatePath 模板路径* @param map 数据集合* @param fileName 文件名* @param response* @throws IOException*/public static void exportExcel(TemplateExportParams templatePath, Map<String, Object> map, String fileName, HttpServletResponse response) throws IOException {Workbook workbook = ExcelExportUtil.exportExcel(templatePath, map);downLoadExcel(fileName, response, workbook);}
新兼容合并单元格方法:
/*** @Description: 合并指定列单元格并导出(合并行)* @Date: /5/24 9:42* @Param templatePath: 模板路径* @Param map: 需要导出的数据map* @Param fileName: 导出文件名称* @Param response: response* @Param sheetMergeParamList: sheet参数集合* @return: void* @Version: 1.0**/public static void exportMergeExcel(TemplateExportParams templatePath, Map<String, Object> map, String fileName, HttpServletResponse response, List<SheetMergeParam> sheetMergeParamList) throws IOException {Workbook workbook = ExcelExportUtil.exportExcel(templatePath, map);//合并单元格mergeExcel(workbook,sheetMergeParamList);downLoadExcel(fileName, response, workbook);}/*** @Description: 合并单元格具体执行方法* @Date: /5/24 14:58* @Param workbook: 工作薄* @Param sheetMergeParamList: sheet集合* @return: void* @Version: 1.0**/private static void mergeExcel(Workbook workbook, List<SheetMergeParam> sheetMergeParamList){for(SheetMergeParam sheetMergeParam : sheetMergeParamList){Sheet sheet = workbook.getSheetAt(sheetMergeParam.getSheetIndex());int lastRowNum = sheet.getLastRowNum();int i;for(i = 0;i < lastRowNum;i++){//获取每行第一个单元格if(null == sheet.getRow(i) || null == sheet.getRow(i).getCell(0)){continue;}Cell cell = sheet.getRow(i).getCell(0);if(sheetMergeParam.getIgnoreCellValues().contains(cell.getStringCellValue()) || StringUtils.isEmpty(cell.getStringCellValue()) ){continue;}//定义合并终止行数int endRowNum = 0;for(int j = i + 1 ;j < lastRowNum;j++){Cell desColumn = sheet.getRow(i).getCell(sheetMergeParam.getDesColumnIndex());Cell nextDesColumn = sheet.getRow(j).getCell(sheetMergeParam.getDesColumnIndex());if(!desColumn.getStringCellValue().equals(nextDesColumn.getStringCellValue())){//值不同,终止此层循环break;}endRowNum ++;}//判断是否有合并项if(endRowNum == 0){continue;}//合并单元格操作for(int z = 0; z < sheetMergeParam.getMergeColumnIndexs().length; z++){//合并起始行,终止行,起始列,终止列int firstRow = i;int lastRow = i + endRowNum;int firstCol = sheetMergeParam.getMergeColumnIndexs()[z];int lastCol = sheetMergeParam.getMergeColumnIndexs()[z];PoiMergeCellUtil.addMergedRegion(sheet,firstRow,lastRow,firstCol,lastCol);}//合并后行号下移i = i + endRowNum;}}}
下面是我的测试类:
@GetMapping("/testExport")public void testExport(HttpServletResponse response) throws Exception{test(response);}private void test(HttpServletResponse response) throws Exception{//中科软Map zkr = Maps.newHashMap();zkr.put("projectNum",4);ContractTest contractTestZkr = new ContractTest("寿险","社保平台",12000,2,"张三",12);ContractTest contractTestZkr1 = new ContractTest("寿险","社保平台",12000,2,"李四",13);ContractTest contractTestZkr2 = new ContractTest("寿险","社保平台",12000,2,"王五",14);zkr.put("list",Lists.newArrayList(contractTestZkr,contractTestZkr1,contractTestZkr2));//博奥特Map bat = Maps.newHashMap();bat.put("projectNum",8);ContractTest contractTestBat = new ContractTest("产险","公积金平台",12000,2,"赵六",13);ContractTest contractTestBat1 = new ContractTest("产险","公积金平台",12000,2,"僧其",16);bat.put("list",Lists.newArrayList(contractTestBat,contractTestBat1));Map map = Maps.newHashMap();map.put("zkr",zkr);map.put("bat",bat);TemplateExportParams templatePath = new TemplateExportParams("template/当月结算考勤汇总测试.xlsx",true);SheetMergeParam sheetMergeParam = new SheetMergeParam(0,0,new int[]{0,1},Lists.newArrayList("公司","合计"));ExcelUtils.exportMergeExcel(templatePath,map,"当月结算考勤汇总测试daochu",response, Lists.newArrayList(sheetMergeParam));}@Data@AllArgsConstructor@NoArgsConstructorpublic class SheetMergeParam implements Serializable{/**** sheet下标*/private int sheetIndex;/**** 合并参考列列号*/private int desColumnIndex;/**** 合并单元格列列号数组*/private int[] mergeColumnIndexs;/**** 忽略行内容(如标题行,合计行等)*/private List<String> ignoreCellValues;}
这个是我的解决方案,如有不正确的地方,欢迎各位指正。
附加
关于导出的样式需要更改的,我贴一篇样式是实例:
/*** @Description: 导出表格样式类* @Date: /4/22 18:02* @Param null:* @return: null* @Version: 1.0**/public class ExcelStyleUtil implements IExcelExportStyler {private static final short STRING_FORMAT = (short) BuiltinFormats.getBuiltinFormat("TEXT");private static final short FONT_SIZE_TEN = 9;private static final short FONT_SIZE_ELEVEN = 10;private static final short FONT_SIZE_TWELVE = 10;/*** 大标题样式*/private CellStyle headerStyle;/*** 每列标题样式*/private CellStyle titleStyle;/*** 数据行样式*/private CellStyle styles;public ExcelStyleUtil(Workbook workbook) {this.init(workbook);}/*** 初始化样式** @param workbook*/private void init(Workbook workbook) {this.headerStyle = initHeaderStyle(workbook);this.titleStyle = initTitleStyle(workbook);this.styles = initStyles(workbook);}/*** 大标题样式** @param color* @return*/@Overridepublic CellStyle getHeaderStyle(short color) {return headerStyle;}/*** 每列标题样式** @param color* @return*/@Overridepublic CellStyle getTitleStyle(short color) {return titleStyle;}/*** 数据行样式** @param parity 可以用来表示奇偶行* @param entity 数据内容* @return 样式*/@Overridepublic CellStyle getStyles(boolean parity, ExcelExportEntity entity) {return styles;}/*** 获取样式方法** @param dataRow 数据行* @param obj对象* @param data 数据*/@Overridepublic CellStyle getStyles(Cell cell, int dataRow, ExcelExportEntity entity, Object obj, Object data) {return getStyles(true, entity);}/*** 模板使用的样式设置*/@Overridepublic CellStyle getTemplateStyles(boolean isSingle, ExcelForEachParams excelForEachParams) {return null;}/*** 初始化--大标题样式** @param workbook* @return*/private CellStyle initHeaderStyle(Workbook workbook) {CellStyle style = getBaseCellStyle(workbook);style.setFont(getFont(workbook, FONT_SIZE_TWELVE, true));return style;}/*** 初始化--每列标题样式** @param workbook* @return*/private CellStyle initTitleStyle(Workbook workbook) {CellStyle style = getBaseCellStyle(workbook);style.setFont(getFont(workbook, FONT_SIZE_ELEVEN, false));//背景色style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());style.setFillPattern(FillPatternType.SOLID_FOREGROUND);return style;}/*** 初始化--数据行样式** @param workbook* @return*/private CellStyle initStyles(Workbook workbook) {CellStyle style = getBaseCellStyle(workbook);style.setFont(getFont(workbook, FONT_SIZE_TEN, false));style.setDataFormat(STRING_FORMAT);return style;}/*** 基础样式** @return*/private CellStyle getBaseCellStyle(Workbook workbook) {CellStyle style = workbook.createCellStyle();//下边框style.setBorderBottom(BorderStyle.THIN);//左边框style.setBorderLeft(BorderStyle.THIN);//上边框style.setBorderTop(BorderStyle.THIN);//右边框style.setBorderRight(BorderStyle.THIN);//水平居中style.setAlignment(HorizontalAlignment.CENTER);//上下居中style.setVerticalAlignment(VerticalAlignment.CENTER);//设置自动换行style.setWrapText(true);return style;}/*** 字体样式** @param size 字体大小* @param isBold 是否加粗* @return*/private Font getFont(Workbook workbook, short size, boolean isBold) {Font font = workbook.createFont();//字体样式font.setFontName("宋体");//是否加粗font.setBold(isBold);//字体大小font.setFontHeightInPoints(size);return font;}}
此处模板导出使用地方:
/*** excel 导出** @param list 数据列表* @param pojoClass pojo类型* @param fileName导出时的excel名称* @param response* @param exportParams 导出参数(标题、sheet名称、是否创建表头,表格类型)*/private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) throws IOException {//改变原始样式ExportParams exportParamsSelf = new ExportParams(exportParams.getTitle(), exportParams.getSheetName(), ExcelType.XSSF);exportParamsSelf.setStyle(ExcelStyleUtil.class);//把数据添加到excel表格中Workbook workbook = ExcelExportUtil.exportExcel(exportParamsSelf, pojoClass, list);downLoadExcel(fileName, response, workbook);}
多sheet表普通导出使用地方:
public ResultDomain testExportMutiSheet(HttpServletResponse response) throws Exception{Workbook workBook = null;List<TestEntityVo> exportList = Lists.newArrayList(new TestEntityVo("中二班","张三",12,"广东深圳"),new TestEntityVo("中二班","李四",15,"湖北武汉"));// 创建参数对象(用来设定excel得sheet得内容等信息)ExportParams deptExportParams = new ExportParams();// 设置sheet得名称deptExportParams.setSheetName("员工表");deptExportParams.setStyle(ExcelStyleUtil.class);// 创建sheet1使用得mapMap<String, Object> deptExportMap = new HashMap<>();// title的参数为ExportParams类型,目前仅仅在ExportParams中设置了sheetNamedeptExportMap.put("title", deptExportParams);// 模版导出对应得实体类型deptExportMap.put("entity", TestEntityVo.class);// sheet中要填充得数据deptExportMap.put("data", exportList);List<TestGoodsVo> emExportList = Lists.newArrayList(new TestGoodsVo("001","辣条",1),new TestGoodsVo("002","方便面",2));ExportParams empExportParams = new ExportParams();empExportParams.setSheetName("货物表");empExportParams.setStyle(ExcelStyleUtil.class);// 创建sheet2使用得mapMap<String, Object> empExportMap = new HashMap<>();empExportMap.put("title", empExportParams);empExportMap.put("entity", TestGoodsVo.class);empExportMap.put("data", emExportList);// 将sheet1、sheet2、sheet3使用得map进行包装List<Map<String, Object>> sheetsList = new ArrayList<>();sheetsList.add(deptExportMap);sheetsList.add(empExportMap);ExcelUtils.exportMutiSheet(sheetsList,"测试",response);return ResultDomain.ok();}public static void exportMutiSheet(List<Map<String,Object>> list,String fileName,HttpServletResponse response) throws IOException{Workbook workbook = ExcelExportUtil.exportExcel(list,ExcelType.XSSF);downLoadExcel(fileName, response, workbook);}
下载:
/*** excel下载** @param fileName 下载时的文件名称* @param response* @param workbook excel数据*/private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {try {response.setCharacterEncoding("UTF-8");response.setHeader("content-Type", "application/vnd.ms-excel");response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));workbook.write(response.getOutputStream());} catch (Exception e) {throw new IOException(e.getMessage());}}