http://dev.w3pub.com/content/2007-5-1/2437.html
上好的人肉包子新鮮出爐啦,各位妖魔鬼怪趕緊來嘗嘗鮮……
首先說說現(xiàn)在我所知道的java編輯Excel文件的兩大開源工具:jakarta
poi和JavaExcelAPI(簡稱JXL),這兩套工具我都試用了一這段時(shí)間,感覺各有優(yōu)劣吧。poi在某些細(xì)節(jié)有些小Bug并且不支持寫入圖片,其他方面都挺不錯(cuò)的;JXL就慘了,除了支持寫入圖片外,我暫時(shí)看不到它比POI好的地方,我碰到的主要的問題就是對(duì)公式支持不是很好,很多帶有公式的Excel文件用JXL打開后,公式就丟失了(比如now(),today()),在網(wǎng)上看到其他大蝦評(píng)論說JXL寫入公式也有問題,另外,JXL操作Excel文件的效率比POI低一點(diǎn)。經(jīng)過比較后,我選擇了poi開發(fā)我的項(xiàng)目。
現(xiàn)在我要做的東西基本完成啦,我把這段時(shí)間使用poi的一些心得總結(jié)出來,希望能對(duì)和我遇到相同問題的朋友有所幫助,少熬幾個(gè)夜,多點(diǎn)時(shí)間陪MM:),至于poi基本的使用方法,自己去看文檔吧。
1、設(shè)置分頁符的bug。
poi里的HSSFSheet類提供了setRowBreak方法可以設(shè)置Sheet的分頁符。
Bug:如果你要設(shè)置分頁符的Sheet是本來就有的,并且你沒有在里面插入過分頁符,那么調(diào)用setRowBreak時(shí)POI會(huì)拋出空指針的異常。
解決方法:在Excel里給這個(gè)sheet插入一個(gè)分頁符,用POI打開后再把它刪掉,然后你就可以隨意插入分頁符了。
如果sheet是由poi生成的則沒有這個(gè)問題。我跟蹤了setRowBreak的源代碼,發(fā)現(xiàn)是Sheet.java下的PageBreakRecord
rowBreaks這個(gè)變量在搞鬼,如果Sheet里原來沒有分頁符,開發(fā)這個(gè)模塊的那位兄臺(tái)忘了為這個(gè)對(duì)象new實(shí)例,所以只能我們先手工給Excel插入一個(gè)分頁符來觸發(fā)poi為rowBreaks創(chuàng)建實(shí)例。
2、如何拷貝行。
我在gmane.org的poi用戶論壇翻遍了每個(gè)相關(guān)的帖子,找遍了api,也沒看到一個(gè)拷貝行的方法,沒辦法,只能自己寫:
//注:this.fWorkbook是一個(gè)HSSHWorkbook,請(qǐng)自行在外部new
public
void copyRows(String pSourceSheetName, String pTargetSheetName, int pStartRow,
int pEndRow, int pPosition)
{
HSSFRow sourceRow = null;
HSSFRow
targetRow = null;
HSSFCell sourceCell = null;
HSSFCell targetCell =
null;
HSSFSheet sourceSheet = null;
HSSFSheet targetSheet =
null;
Region region = null;
int cType;
int i;
short
j;
int targetRowFrom;
int targetRowTo;
if ((pStartRow ==
-1) || (pEndRow == -1))
{
return;
}
sourceSheet =
this.fWorkbook.getSheet(pSourceSheetName);
targetSheet =
this.fWorkbook.getSheet(pTargetSheetName);
//拷貝合并的單元格
for (i = 0; i
< sourceSheet.getNumMergedRegions(); i++)
{
region =
sourceSheet.getMergedRegionAt(i);
if ((region.getRowFrom() >= pStartRow)
&& (region.getRowTo() <= pEndRow))
{
targetRowFrom =
region.getRowFrom() - pStartRow + pPosition;
targetRowTo =
region.getRowTo() - pStartRow +
pPosition;
region.setRowFrom(targetRowFrom);
region.setRowTo(targetRowTo);
targetSheet.addMergedRegion(region);
}
}
//設(shè)置列寬
for
(i = pStartRow; i <= pEndRow; i++)
{
sourceRow =
sourceSheet.getRow(i);
if (sourceRow != null)
{
for (j =
sourceRow.getFirstCellNum(); j < sourceRow.getLastCellNum();
j++)
{
targetSheet.setColumnWidth(j,
sourceSheet.getColumnWidth(j));
}
break;
}
}
//拷貝行并填充數(shù)據(jù)
for
(;i <= pEndRow; i++)
{
sourceRow = sourceSheet.getRow(i);
if
(sourceRow == null)
{
continue;
}
targetRow =
targetSheet.createRow(i - pStartRow +
pPosition);
targetRow.setHeight(sourceRow.getHeight());
for (j =
sourceRow.getFirstCellNum(); j < sourceRow.getLastCellNum();
j++)
{
sourceCell = sourceRow.getCell(j);
if (sourceCell ==
null)
{
continue;
}
targetCell =
targetRow.createCell(j);
targetCell.setEncoding(sourceCell.getEncoding());
targetCell.setCellStyle(sourceCell.getCellStyle());
cType
= sourceCell.getCellType();
targetCell.setCellType(cType);
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:
//parseFormula這個(gè)函數(shù)的用途在后面說明
targetCell.setCellFormula(parseFormula(sourceCell.getCellFormula()));
break;
case
HSSFCell.CELL_TYPE_NUMERIC:
targetCell.setCellValue(sourceCell.getNumericCellValue());
break;
case
HSSFCell.CELL_TYPE_STRING:
targetCell.setCellValue(sourceCell.getStringCellValue());
break;
}
}
}
}
這個(gè)函數(shù)有兩個(gè)問題暫時(shí)無法解決:
a、只能在同一個(gè)Workbook里面使用,跨Workbook總是拷不過去,不知道為什么?
b、由于在拷貝行時(shí)也把行高也拷過去了,如果往這些單元格里寫入的數(shù)據(jù)長度超過單元格長度,那么他們不會(huì)自動(dòng)調(diào)整行高!
有哪位大俠知道上面兩個(gè)問題任意一個(gè)的解決方法,請(qǐng)第一時(shí)間通知我!!!
3、公式的問題。
POI對(duì)Excel公式的支持是相當(dāng)好的,但是我發(fā)現(xiàn)一個(gè)問題,如果公式里面的函數(shù)不帶參數(shù),比如now()或today(),那么你通過getCellFormula()取出來的值就是now(ATTR(semiVolatile))和today(ATTR(semiVolatile)),這樣的值寫入Excel是會(huì)出錯(cuò)的,這也是我上面copyRow的函數(shù)在寫入公式前要調(diào)用parseFormula的原因,parseFormula這個(gè)函數(shù)的功能很簡單,就是把ATTR(semiVolatile)刪掉,我把它的代碼貼出來:
private
String parseFormula(String pPOIFormula)
{
final String
cstReplaceString = "ATTR(semiVolatile)"; //$NON-NLS-1$
StringBuffer result
= null;
int index;
result = new StringBuffer();
index =
pPOIFormula.indexOf(cstReplaceString);
if (index >=
0)
{
result.append(pPOIFormula.substring(0,
index));
result.append(pPOIFormula.substring(index +
cstReplaceString.length()));
}
else
{
result.append(pPOIFormula);
}
return
result.toString();
}
至于為什么會(huì)出現(xiàn)ATTR(semiVolatile),希望哪位大俠現(xiàn)身跟我解釋一下。
4、向Excel寫入圖片的問題。
我上poi論壇查相關(guān)帖子,得到兩種結(jié)論:1、不支持寫入圖片;2、支持寫入圖片,通過EscherGraphics2d這個(gè)Class實(shí)現(xiàn)。于是我就去查EscherGraphics2d這個(gè)Class,發(fā)現(xiàn)這個(gè)Class提供了N個(gè)drawImage方法,喜出望外的我開始寫代碼,結(jié)果調(diào)了一天,一直看不到效果,黔驢技窮的我在萬般無奈下只好跟蹤進(jìn)drawImage這個(gè)函數(shù)內(nèi)部,經(jīng)過N個(gè)函數(shù)調(diào)用后在最底層函數(shù)發(fā)現(xiàn)了最終答案(偶當(dāng)場暴走!!!):
public
boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int
sy1,
int sx2, int sy2, Color bgColor, ImageObserver
imageobserver)
{
if (logger.check( POILogger.WARN
))
logger.log(POILogger.WARN,"drawImage() not supported");
return
true;
}
所以我強(qiáng)烈建議大家,以后使用第三方開發(fā)包一定盡量下載它的源代碼,這樣你在碰到問題時(shí),看看它的的內(nèi)部是怎么實(shí)現(xiàn)的,很多時(shí)候就可以不必重蹈我的覆轍了。既然POI不能寫入圖片,那我們只能把目光投向JXL,我用JXL寫入圖片功能是實(shí)現(xiàn)了,付出的代價(jià)是now()和today()這些函數(shù)丟失掉了,魚與熊掌不能兼得吧,至于怎么解決JXL公式的問題,到下面這個(gè)帖子里和我一起守株待兔吧:
http://community.csdn.net/Expert/topic/3569/3569340.xml?temp=.8480646
好了我所知道得就這些,我接觸poi也才幾星期時(shí)間,上面有些內(nèi)容可能說得并不正確,希望各位大蝦砸磚指正!