你的jsp前面加上这样一句 response.setHeader("content-Disposition","attchment;filename=test.xls");这样的话 打开jsp也面的时候 浏览器会提示下载或打开的对话矿

解决方案 »

  1.   

    首先说说现在我所知道的java编辑Excel文件的两大开源工具:jakarta poi和JavaExcelAPI(简称JXL),这两套工具我都试用了一这段时间,感觉各有优劣吧。poi在某些细节有些小Bug并且不支持写入图片,其他方面都挺不错的;JXL就惨了,除了支持写入图片外,我暂时看不到它比POI好的地方,我碰到的主要的问题就是对公式支持不是很好,很多带有公式的Excel文件用JXL打开后,公式就丢失了(比如now(),today())
    使用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
      

  2.   

    jExcelAPI是一个韩国程序员的作品,虽然没有POI那样血统高贵,但是在笔者的使用过程中,感觉简单方便,对中文支持非常好,功能也比较强大。
    jxl的用法
    搭建环境 将下载后的文件解包,得到jxl.jar,放入classpath,安装就完成了。 基本操作 一、创建文件 拟生成一个名为“测试数据.xls”的Excel文件,其中第一个工作表被命名为“第一页”,大致效果如下:  代码(CreateXLS.java):
      
     //生成Excel的类
     import java.io.*;
     import jxl.*;
     import jxl.write.*; public class CreateXLS
     {
     public static void main(String args[])
     {
     try
     {
      //打开文件
      WritableWorkbook book=
     Workbook.createWorkbook(new File(“测试.xls”));
         
     //生成名为“第一页”的工作表,参数0表示这是第一页
       WritableSheet sheet=book.createSheet(“第一页”,0);
       
     //在Label对象的构造子中指名单元格位置是第一列第一行(0,0)
     //以及单元格内容为test
       Label label=new Label(0,0,”test”);   //将定义好的单元格添加到工作表中
       sheet.addCell(label);   /*生成一个保存数字的单元格
     必须使用Number的完整包路径,否则有语法歧义
     单元格位置是第二列,第一行,值为789.123*/
       jxl.write.Number number = new jxl.write.Number(1,0,789.123);
       sheet.addCell(number);   //写入数据并关闭文件
       book.write();
       book.close();  }catch(Exception e)
      {
       System.out.println(e);
      }
     }
     }  编译执行后,会在当前位置产生一个Excel文件。 三、读取文件 以刚才我们创建的Excel文件为例,做一个简单的读取操作,程序代码如下:
      
     //读取Excel的类
     import java.io.*;
     import jxl.*; public class ReadXLS
     {
     public static void main(String args[])
     {
      try
      {
       Workbook book=
     Workbook.getWorkbook(new File(“测试.xls”));
       
       //获得第一个工作表对象
     Sheet sheet=book.getSheet(0); //得到第一列第一行的单元格
     Cell cell1=sheet.getCell(0,0);
     String result=cell1.getContents();
     System.out.println(result); book.close();  }catch(Exception e)
      {
       System.out.println(e);
      }
     }
     }  程序执行结果:test 四、修改文件 利用jExcelAPI可以修改已有的Excel文件,修改Excel文件的时候,除了打开文件的方式不同之外,其他操作和创建Excel是一样的。下面的例子是在我们已经生成的Excel文件中添加一个工作表: //修改Excel的类,添加一个工作表
     import java.io.*;
     import jxl.*;
     import jxl.write.*; public class UpdateXLS
     {
     public static void main(String args[])
     {
      try
      {
       //Excel获得文件
       Workbook wb=Workbook.getWorkbook(new File(“测试.xls”));
       
       //打开一个文件的副本,并且指定数据写回到原文件
     WritableWorkbook book=
     Workbook.createWorkbook(new File(“测试.xls”),wb);
       
       //添加一个工作表
       WritableSheet sheet=book.createSheet(“第二页”,1);   sheet.addCell(new Label(0,0,”第二页的测试数据”));
       
       book.write();
       book.close();
      }catch(Exception e)
      {
       System.out.println(e);
      }
     }
     }   高级操作 一、 数据格式化 在Excel中不涉及复杂的数据类型,能够比较好的处理字串、数字和日期已经能够满足一般的应用。 1、 字串格式化 字符串的格式化涉及到的是字体、粗细、字号等元素,这些功能主要由WritableFont和WritableCellFormat类来负责。假设我们在生成一个含有字串的单元格时,使用如下语句,为方便叙述,我们为每一行命令加了编号:
     WritableFont font1=
     new WritableFont(WritableFont.TIMES,16,WritableFont.BOLD); ① WritableCellFormat format1=new WritableCellFormat(font1); ② Label label=new Label(0,0,”data 4 test”,format1) ③ 其中①指定了字串格式:字体为TIMES,字号16,加粗显示。WritableFont有非常丰富的构造子,供不同情况下使用,jExcelAPI的java-doc中有详细列表,这里不再列出。 ②处代码使用了WritableCellFormat类,这个类非常重要,通过它可以指定单元格的各种属性,后面的单元格格式化中会有更多描述。 ③处使用了Label类的构造子,指定了字串被赋予那种格式。 在WritableCellFormat类中,还有一个很重要的方法是指定数据的对齐方式,比如针对我们上面的实例,可以指定: //把水平对齐方式指定为居中
     format1.setAlignment(jxl.format.Alignment.CENTRE); //把垂直对齐方式指定为居中
     format1.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE); 二、单元格操作 Excel中很重要的一部分是对单元格的操作,比如行高、列宽、单元格合并等,所幸jExcelAPI提供了这些支持。这些操作相对比较简单,下面只介绍一下相关的API。 1、 合并单元格 WritableSheet.mergeCells(int m,int n,int p,int q);  作用是从(m,n)到(p,q)的单元格全部合并,比如:
     WritableSheet sheet=book.createSheet(“第一页”,0); //合并第一列第一行到第六列第一行的所有单元格
     sheet.mergeCells(0,0,5,0); 合并既可以是横向的,也可以是纵向的。合并后的单元格不能再次进行合并,否则会触发异常。 2、 行高和列宽 WritableSheet.setRowView(int i,int height); 作用是指定第i+1行的高度,比如: //将第一行的高度设为200
     sheet.setRowView(0,200); WritableSheet.setColumnView(int i,int width); 作用是指定第i+1列的宽度,比如: //将第一列的宽度设为30
     sheet.setColumnView(0,30);