这里说一下为什么要选择jxls,而不是poi,因为需求中导出的excel中包含很多种样式、字体等,并且数据是列式动态扩展的,还需要对单元格数据进行判定来标记不同的颜色,这个用poi实现起来比较麻烦,代码量大,后期维护也不方便,jxls很好的解决了我的问题,它采用模板导出的方法,对于数据填充有比较明显的优势,导出性能也比较可观。
注:jxls模板的用法可以去看下官方的APi,http://jxls.sourceforge.net/
http://www.cnblogs.com/klguang/p/6425422.html这篇帖子也比较推荐
好,现在开始。
这里我用的是jxls 2.4.3 ,官方现在已经更新到2.4.5了。
这是我项目中的 jar包依赖
需要导出的excel 模板,只截取了部分
sheet1
sheet2
sheet3
private void createFile(UserMeasRecord userMeasRecord) { System.out.println("正在执行报表生成"); final String applyRecordNum = userMeasRecord.getApplyRecordNum(); String finalReportPath = attachmentAccessFacade.getFileRootPath() + "普通用户完整报告-" + applyRecordNum + ".xlsx"; //创建一个线程池来执行excel计算 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); Callable<Map<String, Object>> c1 = new MyCallable("sony视角",applyRecordNum); Callable<Map<String, Object>> c2 = new MyCallable("统计表&ECN",applyRecordNum); Callable<Map<String, Object>> c3 = new MyCallable("GM-色度",applyRecordNum); Future<Map<String, Object>> f1 = fixedThreadPool.submit(c1); Future<Map<String, Object>> f2 = fixedThreadPool.submit(c2); Future<Map<String, Object>> f3 = fixedThreadPool.submit(c3); try { fixedThreadPool.shutdown(); //等待所有子线程执行完毕 fixedThreadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); try (InputStream is = UserMeasRecordConfigFacadeImpl.class.getResourceAsStream("/Template/user_report_template.xlsx")) { try(OutputStream os = new FileOutputStream(finalReportPath)){ Context sonyContext = new Context(); //sony视角Context sonyContext.putVar("ObjData", f1.get().get("ObjData")); //仅作为颜色判定的数据使用 sonyContext.putVar("styleList", f1.get().get("styleList")); //ECN context Context ecnContext = new Context(); ecnContext.putVar("userEcnContext",f2.get().get("userEcnTableDTO")); //统计表 context Context statisticsContext = new Context(); statisticsContext.putVar("userStatisticsContext",f2.get().get("userStatisticalTableDTO")); //gamma色度context Context gammaContext=new Context(); gammaContext.putVar("gammaDto", f3.get().get("gammaDto")); Transformer transformer = TransformerFactory.createTransformer(is, os); AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer, true); List<Area> xlsAreaList = areaBuilder.build(); for(Area area : xlsAreaList){ String sheetName = area.getStartCellRef().getSheetName(); Area XlsArea = area.getCommandDataList().get(0).getCommand().getAreaList().get(0); //sony视角数据填充 if(sheetName.equals("sony视角")){ List<String[]> list = (List<String[]>) f1.get().get("styleList"); XlsArea.addAreaListener(new SpecDeterminCellAreaListener(transformer, specificationMapper)); area.applyAt(new CellRef("sony视角!A1"), sonyContext); area.processFormulas(); //ECN数据填充 }else if(sheetName.equals("ECN")){ XlsArea.addAreaListener(new HighlightCellAreaListener(XlsArea)); area.applyAt(new CellRef("ECN!B1"), ecnContext); area.processFormulas(); //统计表数据填充 }else if(sheetName.equals("统计表")){ XlsArea.addAreaListener(new HighlightCellAreaListener(XlsArea)); area.applyAt(new CellRef("统计表!B1"), statisticsContext); area.processFormulas(); //gamma数据填充 }else if(sheetName.equals("GM-色度")){ area.applyAt(new CellRef("GM-色度!A1"), gammaContext); area.processFormulas(); } } transformer.write(); ...
//使用jxls导出后可以对excel进行样式处理 } } catch (IOException e){ System.out.println(e); userMeasRecord.setAutoGenReport("生成文件失败"); userMeasRecordMapper.updateByPrimaryKeySelective(userMeasRecord); LOGGER.error("IO异常", e); }catch(Exception e){ System.out.println(e); userMeasRecord.setAutoGenReport("生成文件失败"); userMeasRecordMapper.updateByPrimaryKeySelective(userMeasRecord); LOGGER.error("导出异常",e); } } catch (InterruptedException e) { e.printStackTrace(); userMeasRecord.setAutoGenReport("数据处理失败"); userMeasRecordMapper.updateByPrimaryKeySelective(userMeasRecord); LOGGER.error("等待子线程执行出错", e); } }
这里创建一个线程池数量为5的定长线程池,创建三个future子线程,获取3个子线程计算的数据,使用jxls填充数据到模板。
Callable<Map<String, Object>> c1 = new MyCallable("sony视角",applyRecordNum); Callable<Map<String, Object>> c2 = new MyCallable("统计表&ECN",applyRecordNum); Callable<Map<String, Object>> c3 = new MyCallable("GM-色度",applyRecordNum); Future<Map<String, Object>> f1 = fixedThreadPool.submit(c1); Future<Map<String, Object>> f2 = fixedThreadPool.submit(c2); Future<Map<String, Object>> f3 = fixedThreadPool.submit(c3);重写Callable的call()方法,执行线程任务,返回计算结果,f1.get()获取某个子线程的返回结果,因为我这里三个线程执行是彼此独立的,不需要考虑线程是否安全。
class MyCallable implements Callable<Map<String,Object>>{ private String taskName; private String applyRecordNum; MyCallable(String taskName, String applyRecordNum) { this.taskName = taskName; this.applyRecordNum = applyRecordNum; } @Override public Map<String, Object> call() throws Exception { Map<String,Object> map = null; if(taskName.equals("sony视角")){ map = sonyReportFacade.getSonyReportById(applyRecordNum,null); }else if(taskName.equals("GM-色度")){ map=gammaConfigFacade.generateGammaSheet(applyRecordNum, null); //GM色度 }else if(taskName.equals("统计表&ECN")){ map=reportFacade.getReport(applyRecordNum); //统计表 } return map; } }
下面以ECN sheet模板为例介绍jxls是如何导出的 :
userEcnTableDTO对应模板里面需要填充数据的集合
//ECN context Context ecnContext = new Context(); ecnContext.putVar("userEcnContext",f2.get().get("userEcnTableDTO"));
List<Area> xlsAreaList = areaBuilder.build()获取excel中模板所有数据定义区域,可以用getStartCellRef().getSheetName()获取excel中某个sheet定义的模板,然后填充数据到模板,代码如下:
Transformer transformer = TransformerFactory.createTransformer(is, os); AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer, true); List<Area> xlsAreaList = areaBuilder.build();
Area XlsArea = area.getCommandDataList().get(0).getCommand().getAreaList().get(0);
XlsArea.addAreaListener(new HighlightCellAreaListener(XlsArea)); area.applyAt(new CellRef("ECN!B1"), ecnContext); area.processFormulas();
XlsArea.addAreaListener(new HighlightCellAreaListener(XlsArea)),那这个AreaListener是什么东西呢?
官方给的例子:http://jxls.sourceforge.net/samples/area_listener.html
很容易可以看出官方给出的Demo中就是判断如果
employee.getBonus() >= 0.2
则给当前这个单元格设置一些特殊的样式。
我们需要实现这个AreaListener接口,里面有四个方法,可以去了解下每个的用法,这里不做详细解释。
回到项目,
@Named public class SpecDeterminCellAreaListener implements AreaListener { // @Inject // private SrcParamNumExtMapper srcParamNumExtMapper; private int a; private PoiTransformer transformer; private SpecificationMapper specificationMapper; public SpecDeterminCellAreaListener(){ } public SpecDeterminCellAreaListener(Transformer transformer, SpecificationMapper specificationMapper){ this.transformer = (PoiTransformer) transformer; this.specificationMapper = specificationMapper; } @Override public void beforeApplyAtCell(CellRef cellRef, Context context) { // TODO Auto-generated method stub } @Override public void afterApplyAtCell(CellRef cellRef, Context context) { // TODO Auto-generated method stub } @Override public void beforeTransformCell(CellRef srcCell, CellRef targetCell, Context context) { // TODO Auto-generated method stub } @Override public void afterTransformCell(CellRef srcCell, CellRef targetCell, Context context) { System.out.println("Source: " + srcCell.getCellName() + ", Target: " + targetCell.getCellName()); List<String[]> userSonyList = (List<String[]>) context.getVar("styleList"); if(!userSonyList.isEmpty()){ sonyColorCell(targetCell,userSonyList.get(a)[0],userSonyList.get(a)[1],userSonyList.get(a)[2]); }else{ sonyColorCell(targetCell,null,null,null); } a++; } @SuppressWarnings("deprecation") public void sonyColorCell(CellRef cellRef,String dhOrdc,String ReportParam,String value){ XSSFWorkbook workbook = (XSSFWorkbook) transformer.getWorkbook(); XSSFDataFormat fmt = workbook.createDataFormat(); Sheet sheet = workbook.getSheet(cellRef.getSheetName()); sheet.setDefaultColumnWidth(30); Cell cell = sheet.getRow(cellRef.getRow()).getCell(cellRef.getCol()); CellStyle cellStyle = cell.getCellStyle(); XSSFCellStyle newCellStyle = workbook.createCellStyle(); newCellStyle.setDataFormat(fmt.getFormat("#,##0.00")); newCellStyle.setFont( workbook.getFontAt( cellStyle.getFontIndex())); newCellStyle.setBorderBottom(cellStyle.getBorderBottomEnum()); newCellStyle.setBorderTop(cellStyle.getBorderTopEnum()); newCellStyle.setBorderLeft(cellStyle.getBorderLeftEnum()); newCellStyle.setBorderRight(cellStyle.getBorderRightEnum()); newCellStyle.setWrapText(true); if(dhOrdc !=null && ReportParam!=null && value !=null){ try { ...
//部分代码省略 if (dhOrdc.equalsIgnoreCase("dc")) { if (BigValue.compareTo(DcFair) == 1) { newCellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); // newCellStyle.setFillBackgroundColor(cellStyle.getFillBackgroundColor()); newCellStyle.setFillForegroundColor(new XSSFColor(new Color(100,170,70))); } else if (BigValue.compareTo(DcPoor) == -1) { newCellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); // newCellStyle.setFillBackgroundColor(cellStyle.getFillBackgroundColor()); newCellStyle.setFillForegroundColor(new XSSFColor(new Color(255,192,0))); } else if (BigValue.compareTo(DcFair) == -1 && BigValue.compareTo(DcPoor) == 1) { newCellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); // newCellStyle.setFillBackgroundColor(cellStyle.getFillBackgroundColor()); newCellStyle.setFillForegroundColor(new XSSFColor(new Color(255,255,255))); } } else { if (BigValue.compareTo(DhRPoor) == -1) { newCellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); // newCellStyle.setFillBackgroundColor(cellStyle.getFillBackgroundColor()); newCellStyle.setFillForegroundColor(new XSSFColor(new Color(228,64,80))); } else if (BigValue.compareTo(DhRPoor) == 1 && BigValue.compareTo(DhReddish) == -1) { newCellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); // newCellStyle.setFillBackgroundColor(cellStyle.getFillBackgroundColor()); newCellStyle.setFillForegroundColor(new XSSFColor(new Color(251,183,238))); } else if (BigValue.compareTo(DhGPoor) == 1) { newCellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); // newCellStyle.setFillBackgroundColor(cellStyle.getFillBackgroundColor()); newCellStyle.setFillForegroundColor(new XSSFColor(new Color(80,130,50))); } else if (BigValue.compareTo(DhGreenish) == 1 && BigValue.compareTo(DhGPoor) == -1) { newCellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); // newCellStyle.setFillBackgroundColor(cellStyle.getFillBackgroundColor()); newCellStyle.setFillForegroundColor(new XSSFColor(new Color(180,240,180))); } else if (BigValue.compareTo(DhReddish) == 1 && BigValue.compareTo(DhGreenish) == -1) { newCellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND); // newCellStyle.setFillBackgroundColor(cellStyle.getFillBackgroundColor()); newCellStyle.setFillForegroundColor(new XSSFColor(new Color(255,255,255))); } } } } catch (Exception e) { e.printStackTrace(); } } cell.setCellStyle(newCellStyle); }
根据当前单元格的数据和标准数据做比较,如果符合就给当前单元格设置背景色
newCellStyle.setFillForegroundColor(new XSSFColor(new Color(100,170,70)));
完。
最后说明一下:
1.jxls会自动根据你model中put的值来判断写入进excel中的是字符串还是数值。
2.可以在模板中使用${model*1},将文本类型转为数值类型。