用多线程实现jxls导出excel

这里说一下为什么要选择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会自动根据你modelput的值来判断写入进excel中的是字符串还是数值。

  2.可以在模板中使用${model*1},将文本类型转为数值类型。 

    


猜你喜欢

转载自blog.csdn.net/rixingbeioul46364/article/details/80536481