上好的人肉包子新鲜出炉啦,各位妖魔鬼怪赶紧来尝尝鲜……
首先说说现在我所知道的java编辑excel文件的两大开源工具:jakarta poi和javaexcelapi(简称jxl),这两套工具我都试用了一这段时间,感觉各有优劣吧。poi在某些细节有些小bug并且不支持写入图片,其他方面都挺不错的;jxl就惨了,除了支持写入图片外,我暂时看不到它比poi好的地方,我碰到的主要的问题就是对公式支持不是很好,很多带有公式的excel文件用jxl打开后,公式就丢失了(比如now(),today()),在网上看到其他大虾评论说jxl写入公式也有问题,另外,jxl操作excel文件的效率比poi低一点。经过比较后,我选择了poi开发我的项目。
现在我要做的东西基本完成啦,我把这段时间使用poi的一些心得总结出来,希望能对和我遇到相同问题的朋友有所帮助,少熬几个夜,多点时间陪mm:),至于poi基本的使用方法,自己去看文档吧。
1、设置分页符的bug。
poi里的hssfsheet类提供了setrowbreak方法可以设置sheet的分页符。
bug:如果你要设置分页符的sheet是本来就有的,并且你没有在里面插入过分页符,那么调用setrowbreak时poi会抛出空指针的异常。
解决方法:在excel里给这个sheet插入一个分页符,用poi打开后再把它删掉,然后你就可以随意插入分页符了。
如果sheet是由poi生成的则没有这个问题。我跟踪了setrowbreak的源代码,发现是sheet.java下的pagebreakrecord rowbreaks这个变量在搞鬼,如果sheet里原来没有分页符,开发这个模块的那位兄台忘了为这个对象new实例,所以只能我们先手工给excel插入一个分页符来触发poi为rowbreaks创建实例。
2、如何拷贝行。
我在gmane.org的poi用户论坛翻遍了每个相关的帖子,找遍了api,也没看到一个拷贝行的方法,没办法,只能自己写:
//注:this.fworkbook是一个hsshworkbook,请自行在外部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);
}
}
//设置列宽
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;
}
}
//拷贝行并填充数据
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这个函数的用途在后面说明
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;
}
}
}
}
这个函数有两个问题暂时无法解决:
a、只能在同一个workbook里面使用,跨workbook总是拷不过去,不知道为什么?
b、由于在拷贝行时也把行高也拷过去了,如果往这些单元格里写入的数据长度超过单元格长度,那么他们不会自动调整行高!
有哪位大侠知道上面两个问题任意一个的解决方法,请第一时间通知我!!!
3、公式的问题。
poi对excel公式的支持是相当好的,但是我发现一个问题,如果公式里面的函数不带参数,比如now()或today(),那么你通过getcellformula()取出来的值就是now(attr(semivolatile))和today(attr(semivolatile)),这样的值写入excel是会出错的,这也是我上面copyrow的函数在写入公式前要调用parseformula的原因,parseformula这个函数的功能很简单,就是把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();
}
至于为什么会出现attr(semivolatile),希望哪位大侠现身跟我解释一下。
4、向excel写入图片的问题。
我上poi论坛查相关帖子,得到两种结论:1、不支持写入图片;2、支持写入图片,通过eschergraphics2d这个class实现。于是我就去查eschergraphics2d这个class,发现这个class提供了n个drawimage方法,喜出望外的我开始写代码,结果调了一天,一直看不到效果,黔驴技穷的我在万般无奈下只好跟踪进drawimage这个函数内部,经过n个函数调用后在最底层函数发现了最终答案(偶当场暴走!!!):
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;
}
所以我强烈建议大家,以后使用第三方开发包一定尽量下载它的源代码,这样你在碰到问题时,看看它的的内部是怎么实现的,很多时候就可以不必重蹈我的覆辙了。既然poi不能写入图片,那我们只能把目光投向jxl,我用jxl写入图片功能是实现了,付出的代价是now()和today()这些函数丢失掉了,鱼与熊掌不能兼得吧,至于怎么解决jxl公式的问题,到下面这个帖子里和我一起守株待兔吧:
http://community.csdn.net/expert/topic/3569/3569340.xml?temp=.8480646
好了我所知道得就这些,我接触poi也才几星期时间,上面有些内容可能说得并不正确,希望各位大虾砸砖指正!
闽公网安备 35060202000074号