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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章