用java完成将数据导出到Excel中的功能,首先去了解一下有哪些Java ExcelAPI。

Java Aspose Cells

        Java Aspose Cells 是一种纯粹的Java授权的Excel API,开发和供应商Aspose发布。这个API的最新版本是8.1.2,发布于2014年7月,是一个丰富而厚重的API(普通Java类和AWT类的组合)设计,可以读、写和操纵电子表格Excel的组件。此API常见用途如下:

  • Excel报表,建立动态Excel报表
  • 高保真的Excel渲染和打印
  • 从Excel电子表格中导入和导出数据
  • 生成,编辑,转换和电子表格

JXL

        JXL是一款专为Selenium第三方框架,支持基于Web浏览器(数据是Web浏览器自动更新)数据驱动的自动化。然而,它也被用来作为JExcel API的一个共同的支持库,因为它的基本功能是可创建,读取和写入电子表格。基本特征如下:

  • 生成Excel文件
  • 从工作簿和电子表格导入数据
  • 获得行和列的总数
  • 注意:JXL只支持xls档案格式,并且不能处理大数据量。

JExcel

        JExcel是由Team Dev开发提供纯行货API。利用这一点程序员可以很容易地读取,写入,显示和修改Excel工作簿中的两种格式:.xls和.XLSX。这个API可以很容易地嵌入Java的Swing和AWT。这个API的最新版本是Jexcel-2.6.12,发布于2009年,主要特点如下:

  • 自动化Excel应用程序,工作簿,工作表等
  • 在Java Swing应用程序作为普通的Swing组件嵌入到工作簿
  • 事件侦听器添加到工作簿和电子表格
  • 添加事件处理程序来处理的工作簿和电子表格事件的行为
  • 添加本地对等开发定制功能

Apache POI

        Apache POI是Apache软件基金会提供的100%开源库。大多数中小规模的应用程序开发主要依赖于Apache POI(HSSF+ XSSF)。它支持Excel 库的所有基本功能; 然而,呈现和文本提取是它的主要特点。


        这次我用到的是最后一种Apache POI。首先是一个总的方法exportExcel_2007,传入4个主要参数,分别是: sheetName 工作表的名称;dataset 数据源;fieldMap 类的英文属性和Excel中的中文列名的对应关系;sheetSize 每个工作表中记录的最大个数。
代码如下:

/**
   * @param sheetName 工作表的名称
   * @param dataset 数据源
   * @param fieldMap 类的英文属性和Excel中的中文列名的对应关系
   * @param sheetSize 每个工作表中记录的最大个数
   * @return
   * @throws Exception
   */
  public <T> Workbook exportExcel_2007(String sheetName, List<T> dataset,
                                             LinkedHashMap<String, String> fieldMap, int sheetSize, List<ModelAttr> modelAttrs) throws Exception {
    if(dataset==null || dataset.size()==0) {
      throw new RuntimeException("没有任何数据");
    }

    SXSSFWorkbook wb = new SXSSFWorkbook(1000);

    //1.计算一共有多少个工作表
    double sheetNum = Math.ceil(dataset.size()/new Integer(sheetSize).doubleValue());

    //2.创建相应的工作表,并向其中填充数据
    for(int i=0; i<sheetNum; i++){
      //如果只有一个工作表的情况
      if(1==sheetNum){
        Sheet sheet = wb.createSheet(sheetName);
        fillSheet(sheet, dataset, fieldMap, 0, dataset.size()-1,modelAttrs);
        setColumnBorderAndColor(wb,sheet);
        //有多个工作表的情况
      }else{
        Sheet sheet = wb.createSheet(sheetName+(i+1));
        //获取开始索引和结束索引
        int firstIndex=i*sheetSize;
        int lastIndex=(i+1)*sheetSize-1>dataset.size()-1 ? dataset.size()-1 : (i+1)*sheetSize-1;
        //填充工作表
        fillSheet(sheet, dataset, fieldMap, firstIndex, lastIndex,modelAttrs);
        setColumnBorderAndColor(wb,sheet);
      }
    }
    return wb;
  }

        将工作表创建出来,就需要对表头和表体进行填充。因为我这里的表头是动态的,因此多传入了一个参数进行相关判断。填充表头和表体的函数是fillSheet。

代码如下:

/**
   * @MethodName  : fillSheet
   * @Description : 向工作表中填充数据
   * @param sheet       工作表
   * @param list        数据源
   * @param fieldMap    中英文字段对应关系的Map
   * @param firstIndex  开始索引
   * @param lastIndex   结束索引
   */
  private <T> void fillSheet(
          Sheet sheet,
          List<T> list,
          LinkedHashMap<String,String> fieldMap,
          int firstIndex,
          int lastIndex,
          List<ModelAttr> modelAttrs
  )throws Exception{

    //定义存放英文字段名和中文字段名的数组
    String[] enFields=new String[fieldMap.size()];
    String[] cnFields=new String[fieldMap.size()];

    //填充数组
    int count=0;
    for(Map.Entry<String,String> entry:fieldMap.entrySet()){
      enFields[count]=entry.getKey();
      cnFields[count]=entry.getValue();
      count++;
    }

    //填充表头
    Row firstRow = sheet.createRow(0);
    for(int i=0;i<cnFields.length;i++){
      firstRow.createCell(i).setCellValue(cnFields[i]);
    }

    //填充内容
    int rowNo=1;
    for(int index=firstIndex;index<=lastIndex;index++){
      Row row = sheet.createRow(rowNo);
      //获取单个对象
      Map<String,Object> item= (Map<String, Object>) list.get(index);
      for(int i=0;i<enFields.length;i++){
        ModelAttrType type = null;
        for(ModelAttr modelAttr : modelAttrs){
          if(enFields[i].equals(modelAttr.getAttrCode())){
              type = modelAttr.getAttrType();
          }
        }
        String value = getFieldValueByNameSequence(enFields[i], item,type);
        String fieldValue=value==null ? "" : value;
        row.createCell(i).setCellValue(fieldValue);
      }
      rowNo++;
    }
    //设置自动列宽
    setColumnAutoSize(sheet, 512);


  }

        首先将表头的数据放入两个数组中,一个是中文表头,一个是英文code,用来匹配表体。然后中文表体填充到新建的firstRow中。

        在表头完成后,根据数据源填充表体,因为数据源是一个复杂的mapList,所以需要对填充的value进行提取处理。我这里写了几个方法,其中针对不同类型的数据处理写了一个感知器,感知器的不用类型处理的代码太多,就不贴了。总之能把你想要放的数据取出来就可以,不用写很复杂。


代码如下:

/**
   * @MethodName  : getFieldValueByNameSequence
   * @Description :
   * 根据属性名获取属性值
   *
   * @param fieldNameSequence  简单属性名
   * @param o 对象
   * @return  属性值
   * @throws Exception
   */
  private String getFieldValueByNameSequence(String fieldNameSequence, Map<String,Object> o,ModelAttrType type) throws Exception{
    String value=null;
    return value = getFieldValueByName(fieldNameSequence,o,type);
  }


/**
   * @MethodName  : getFieldValueByName
   * @Description : 根据字段名获取字段值
   * @param fieldName 字段名
   * @param o 对象
   * @return  字段值
   */
  private String getFieldValueByName(String fieldName, Map<String,Object> o,ModelAttrType type) throws Exception{
    Object value=null;
    value=getFieldByName(fieldName, o);
    String valueStr = insAttrValueAwareProcessor.convertToExcel(type, value);
    return valueStr;
  }


  /**
   * @MethodName  : getFieldByName
   * @Description : 根据字段名获取字段
   * @param fieldName 字段名
   * @param
   * @return 字段
   */
  private static Object getFieldByName(String fieldName, Map<String,Object>  o){
    if(o == null){
      return null;
    }
    return o.get(fieldName);
  }

        填充完数据后,就需要简单设置一下工作表的样式,具体看实际情况。
代码如下:
  /**
   * @MethodName  : setColumnAutoSize
   * @Description : 设置工作表自动列宽和首行加粗
   * @param ws
   */
  private static void setColumnAutoSize(Sheet ws,int extraWith){
    //获取本列的最宽单元格的宽度
    for(int i=0;i<ws.getRow(0).getPhysicalNumberOfCells();i++){
      int colWith=0;
      for(int j=0;j<ws.getLastRowNum();j++){
        String content=ws.getRow(j).getCell(i).getStringCellValue();
        int cellWith=content.length();
        if(colWith<cellWith){
          colWith=cellWith;
        }
      }
      //设置单元格的宽度为最宽宽度+额外宽度
      ws.setColumnWidth(i, colWith*extraWith);
    }
  }



  /**
   * 设置工作表单元格边框和首行背景色
   * @param wb
   * @param ws
   */
  private static void setColumnBorderAndColor(SXSSFWorkbook wb,Sheet ws){
    XSSFCellStyle cellBorderStyle = (XSSFCellStyle)wb.createCellStyle();
    XSSFCellStyle cellColorStyle = (XSSFCellStyle)wb.createCellStyle();
    for(int i=0;i<ws.getPhysicalNumberOfRows();i++){
      Row row = ws.getRow(i);
      for(int j=0;j<ws.getRow(0).getPhysicalNumberOfCells();j++){
        Cell cell = row.getCell(j);
        Cell cellFirst = ws.getRow(0).getCell(j);
        //给单元格设置边框
        cellBorderStyle.setBorderBottom(BorderStyle.THIN);
        cellBorderStyle.setBorderTop(BorderStyle.THIN);
        cellBorderStyle.setBorderLeft(BorderStyle.THIN);
        cellBorderStyle.setBorderRight(BorderStyle.THIN);
        //给第一行设置边框和背景
        cellColorStyle.setFillForegroundColor(IndexedColors.AQUA.getIndex());
        cellColorStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        cellColorStyle.setBorderBottom(BorderStyle.THIN);
        cellColorStyle.setBorderTop(BorderStyle.THIN);
        cellColorStyle.setBorderLeft(BorderStyle.THIN);
        cellColorStyle.setBorderRight(BorderStyle.THIN);
        cell.setCellStyle(cellBorderStyle);
        cellFirst.setCellStyle(cellColorStyle);
      }
    }
  }

        有想深入了解学习Apache POI的,可以看看这篇教程Apache POI教程。我也对其进行了参考学习。