poi的那些事儿

 

POI是 血统高贵的Apache下的开源项目,仰视一下。它可以轻松处理一般的excel读取、导出、和一般的报表操作,但是涉及到一些excel的不常用的特性时又有点复杂。这不怪POI,只能说excel身就是个非常复杂的东西,何况POI还要处理微软Office和开放标准的兼容问题。折腾了一个多星期,写点东西吧。进入正题。POI版本为poi-3.5-FINAL-20090928.jar


从文件读取excel

      public HSSFWorkbook readWorkbook(File file) throws IOException, FileNotFoundException {
   ByteArrayOutputStream byteOS = new ByteArrayOutputStream();
   FileInputStream fis = new FileInputStream(file);
   byte[] by = new byte[512];
   int t = fis.read(by, 0, by.length);
   while (t > 0) {
    byteOS.write(by, 0, 512); // read 512
    t = fis.read(by, 0, by.length);
   }
   byteOS.close();
   InputStream byteIS = new ByteArrayInputStream(byteOS.toByteArray());
   HSSFWorkbook wbDest = new HSSFWorkbook(byteIS);
   return wbDest;
}


将HSSFWorkbook 写入文件

       public void saveWorkbook(File file, HSSFWorkbook targetWb) throws FileNotFoundException, IOException {
   // 目标填充文件
   FileOutputStream fos = new FileOutputStream(file);
   // 写文件
   targetWb.write(fos);
   fos.flush();
   fos.close();
}


对sheet中所有的单元格(包括合并单元格)根据字数设置合适的行高

在报表开发中,有时需要对合并的单元格调整行高,使内容全部展示出来。在单个普通单元格中,可以设置cell.getCellStyle.setWrapText(true)实现自动换行,且自动调整行高,但是这对合并的单元格似乎没有效果。附上自己写的一个方法,貌似效率不高。
     /**
     *对sheet中所有的单元格(包括合并单元格)根据字数设置合适的行高
     */
     public void adjustRowHeight(HSSFSheet stTpt) {
   // 默认行间距,pixel为单位
   float defaultRowGapInPoint = 4f;
   // 得到所有的合并区域
   List<Region> regions = new ArrayList<Region>();
   for (int i = 0; i < stTpt.getNumMergedRegions(); i++) {
    regions.add(stTpt.getMergedRegionAt(i));
   }
   int rows = stTpt.getPhysicalNumberOfRows();
   for (int r = 0; r < rows; r++) {
    HSSFRow row = stTpt.getRow(r);
    int cells = row.getPhysicalNumberOfCells();
    for (short c = 0; c < cells; c++) {
     HSSFCell cell = row.getCell(c);
     // 只对String类型单元格调整高度
     if (cell != null && cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
      boolean isBelongToRegion = false;
      for (Region region : regions) {
       if (region.contains(r, c)) {
        isBelongToRegion = true;
        int rowFrom = region.getRowFrom();
        int rowTo = region.getRowTo();
        int colFrom = region.getColumnFrom();
        int colTo = region.getColumnTo();
        int regionWidths = 0;
        for (int ii = colFrom; ii <= colTo; ii++) {
         regionWidths += stTpt.getColumnWidth(ii);
        }
        long stringWidths = cell.getRichStringCellValue().toString().getBytes().length * 256;
        // 计算所需高度为默认高度的多少倍
        long aRows = stringWidths / regionWidths + 1;
                                                        //得到每行应该有的高度
        HSSFFont font = cell.getCellStyle().getFont(stTpt.getWorkbook());
        float rowHeightInPoint = font.getFontHeightInPoints() + defaultRowGapInPoint;
        float height = rowHeightInPoint * aRows / (rowTo - rowFrom + 1);
        for (int jj = rowFrom; jj <= rowTo; jj++) {
         HSSFRow RegionRow = stTpt.getRow(jj);
         if (RegionRow.getHeightInPoints() < height) {
          RegionRow.setHeightInPoints(height);
         }
        }
        break;
       }
      }
      if (!isBelongToRegion) {
       long stringWidths = cell.getRichStringCellValue().toString().getBytes().length * 256;
                                                //得到列宽为一个字符的1/256
       long colWidth = stTpt.getColumnWidth(c);
       long aRows = stringWidths / colWidth + 1;
                                                //得到每行应该有的高度
       HSSFFont font = cell.getCellStyle().getFont(stTpt.getWorkbook());
       float rowHeightInPoint = font.getFontHeightInPoints() + defaultRowGapInPoint;
       if (row.getHeightInPoints() < aRows * rowHeightInPoint) {
        row.setHeightInPoints(aRows * rowHeightInPoint);
       }
      }
     }
    }
   }
}
如果单元格的类型为“文本",对应的cellStyle中的dataFormat为“@”或这“Text”,那么当内容很多时,excel会用“####”来代替,而不管你是否已经为其调整出了显示区域。对应的解决方案为设置单元格为“常规”类型,对应的cellStyle中的dataFormat为“General”,程序实现类似cell.getCellStyle.setDataFormat(targetWorkbook.createDataFormat().getFormat("General"))。这样就不会出现“###########“了。


调整合并单元格的样式,基本上是边框

       public void addMergedRegionStyle(HSSFSheet targetSheet, Region region, HSSFCellStyle style) {
   int rowFrom = region.getRowFrom();
   int rowTo = region.getRowTo();
   int colFrom = region.getColumnFrom();
   int colTo = region.getColumnTo();
   for (int r = rowFrom; r <= rowTo; r++) {
    HSSFRow row = targetSheet.getRow(r);
    if (row != null) {
     for (int c = colFrom; c <= colTo; c++) {
      HSSFCell cell = row.getCell(c);
      if (cell == null) {
       cell = row.createCell(c);
      }
      cell.getCellStyle().setBorderLeft(style.getBorderLeft());
      cell.getCellStyle().setBorderRight(style.getBorderRight());
      cell.getCellStyle().setBorderTop(style.getBorderTop());
      cell.getCellStyle().setBorderBottom(style.getBorderBottom());
     }
    }
   }
}


为单元格或者合并单元格添加合法性限制的下拉列表

摘点关键代码
               String[] list = { "是", "否", "随便" };
               // 只对(0,0)单元格有效
   CellRangeAddressList regions = new CellRangeAddressList(0,0,0,0);
   // // 生成下拉框内容
   DVConstraint constraint = DVConstraint.createExplicitListConstraint(list);
   // // 绑定下拉框和作用区域
   HSSFDataValidation dataValidation = new HSSFDataValidation(regions,constraint);
   // 对sheet页生效
   targetSt.addValidationData(dataValidation );
这种方法在创建新的excel的时候没有问题,可以创建下来列表,但是当sheet是从创建好的MS Office excel 2003 中读取的话,就会出问题啦。/跑出一个傻乐吧唧的异常:java.lang.IllegalStateException: Unexpected (org.apache.poi.hssf.record.UnknownRecord)好像是POI对微软格式的excel支持不好导致的。
如果仍然想实现下拉列表的功能,可能就要换个思路了,什么思路呢,我的做法是重新创建了一个sheet,然后在把原来sheet中的内容拷贝过来,这时再添加列表就没有问题了。

下面奉上一个拷贝sheet的方法。


copy已有workbook中的第一个sheet到新的workbook中的第一个sheet

      public HSSFWorkbook createNewWorkBook(HSSFWorkbook sourceWb) {
   HSSFSheet sourceSheet = sourceWb.getSheetAt(0);
   HSSFWorkbook targetWb = new HSSFWorkbook();
   HSSFSheet targetSheet = targetWb.createSheet();
   HSSFRow sourceRow = null;
   HSSFRow targetRow = null;
   HSSFCell sourceCell = null;
   HSSFCell targetCell = null;
   Region region = null;
   int pEndRow = sourceSheet.getLastRowNum();

   // 拷贝合并的单元格
   for (int i = 0; i < sourceSheet.getNumMergedRegions(); i++) {
    region = sourceSheet.getMergedRegionAt(i);
    if ((region.getRowFrom() >= 0) && (region.getRowTo() <= pEndRow)) {
     targetSheet.addMergedRegion(Region.convertToCellRangeAddress(region));
    }
   }

   // 拷贝行并填充数据
   for (int i = 0; i <= pEndRow; i++) {
    sourceRow = sourceSheet.getRow(i);
    if (sourceRow == null) {
     continue;
    }
    int targetRowIndex = i;
    targetRow = targetSheet.createRow(targetRowIndex);
    targetRow.setHeight(sourceRow.getHeight());
    for (int j = sourceRow.getFirstCellNum(); j <= sourceRow.getPhysicalNumberOfCells(); j++) {
     sourceCell = sourceRow.getCell(j);
     if (sourceCell == null) {
      continue;
     }
     targetSheet.setColumnWidth((int) j, sourceSheet.getColumnWidth((int) j));
     targetSheet.setActive(sourceSheet.isActive());
     targetSheet.setColumnHidden(j, sourceSheet.isColumnHidden(j));
     targetCell = targetRow.createCell(j);
     int cType = sourceCell.getCellType();
     targetCell.setCellType(cType);
     if (sourceCell.getHyperlink() != null)
      targetCell.setHyperlink(sourceCell.getHyperlink());
     if (sourceCell.getCellComment() != null)
      targetCell.setCellComment(sourceCell.getCellComment());
     HSSFFont srcFont = sourceCell.getCellStyle().getFont(sourceWb);
     //targetCell.setCellStyle(this.copyCellStyle(targetWb, sourceCell.getCellStyle(), srcFont));
     targetCell.getCellStyle().cloneStyleFrom(sourceCell.getCellStyle());
     switch (cType) {
     case HSSFCell.CELL_TYPE_BOOLEAN:
      targetCell.setCellValue(sourceCell.getBooleanCellValue());
      break;
     case HSSFCell.CELL_TYPE_ERROR:
      targetCell.setCellErrorValue(sourceCell.getErrorCellValue());
      break;
     case HSSFCell.CELL_TYPE_FORMULA:
      String s = sourceCell.getCellFormula();
      s = s.replaceAll(String.valueOf(i + 1), String.valueOf(targetRowIndex + 1));
      targetCell.setCellFormula(s);
      break;
     case HSSFCell.CELL_TYPE_NUMERIC:
      targetCell.setCellValue(sourceCell.getNumericCellValue());
      break;
     case HSSFCell.CELL_TYPE_STRING:
      targetCell.setCellValue(sourceCell.getRichStringCellValue());
      break;
     }
    }
   }
   return targetWb;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值