POI是 血統高貴的Apache下的開源項目,仰視一下。它可以輕鬆處理一般的excel讀取、導出、和一般的報表操作,但是涉及到一些excel的不常用的特性時又有點複雜。這不怪POI,只能說excel身就是個非常複雜的東西,何況POI還要處理微軟Office和開放標準的兼容問題。折騰了一個多星期,寫點東西吧。進入正題。POI版本爲poi-3.5-FINAL-20090928.jar
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;
}