这里说一下为什么要选择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
//使用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> c1 = new MyCallable("sony视角",applyRecordNum);
Callable> c2 = new MyCallable("统计表&ECN",applyRecordNum);
Callable> c3 = new MyCallable("GM-色度",applyRecordNum);
Future> f1 = fixedThreadPool.submit(c1);
Future> f2 = fixedThreadPool.submit(c2);
Future> f3 = fixedThreadPool.submit(c3);
重写Callable的call()方法,执行线程任务,返回计算结果,f1.get()获取某个子线程的返回结果,因为我这里三个线程执行是彼此独立的,不需要考虑线程是否安全。
class MyCallable implements Callable>{
private String taskName;
private String applyRecordNum;
MyCallable(String taskName, String applyRecordNum) {
this.taskName = taskName;
this.applyRecordNum = applyRecordNum;
}
@Override
public Map call() throws Exception {
Map 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 xlsAreaList = areaBuilder.build()获取excel中模板所有数据定义区域,可以用getStartCellRef().getSheetName()获取excel中某个sheet定义的模板,然后填充数据到模板,代码如下:
Transformer transformer = TransformerFactory.createTransformer(is, os);
AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer, true);
List 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 userSonyList = (List) 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},将文本类型转为数值类型。