在线实时生成Excel文件流供下载

    技术2022-05-11  73

    我做过的一个项目,需要实现在线实时生成 Excel文件供客户端下载的需求,最初考虑的是先在服务器端生成真实的文件,然后在客户端下载该文件。后来发现这样做不但性能不够好、速度较慢,而且还要占用服务器空间。所以采取了在服务器端生成文件输出流(ServletOutputStream),通过HttpServletResponse对象设置相应的响应头,然后将此输出流传往客户端的方法实现。在实现过程中,用到了Apache组织的Jakarta开源组件POI,读者朋友可到其官方网站查阅相关资料及下载。现将整个实现过程介绍如下:         一、首先,根据Excel表的特点,我编写了一个Excel表模型类ExcelModel,代码如下:

    package  com.qnjian.myprj.excel; import  java.util.ArrayList; /** * * <p>Title: ExcelModel</p> * * <p>Description: Excel表的操作模型</p> * * <p>Copyright: Copyright (c) 2005-10-20</p> * * <p>Company: *** </p> * * @author zzj * @version 1.0 */ public   class  ExcelModel  {      /**       * 文件路径,这里是包含文件名的路径       */      protected String path;      /**       * 工作表名       */      protected String sheetName;      /**       * 表内数据,保存在二维的ArrayList对象中       */      protected ArrayList data;      /**       * 数据表的标题内容       */      protected ArrayList header;      /**       * 用于设置列宽的整型数组       * 这个方法在程序中暂未用到       * 适用于固定列数的表格       */      protected int[] width;      public ExcelModel() {            path="report.xls";      }      public ExcelModel(String path) {           this.path=path;     }      public void setPath(String path){           this.path=path;      }      public String getPath(){            return this.path;      }            public void setSheetName(String sheetName){          this.sheetName=sheetName;      }            public String getSheetName(){          return this.sheetName;      }      public void setData(ArrayList data){            this.data=data;      }      public ArrayList getData(){            return this.data;      }      public void setHeader(ArrayList header){            this.header=header;      }      public ArrayList getHeader(){            return this.header;      }      public void setWidth(int[] width){            this.width=width;      }      public int[] getWidth(){            return this.width;      }}

            二、编写一个下载接口ExcelDownLoad,定义基本的方法:

    package  com.qnjian.myprj.excel; import  java.io.IOException; import  java.util.List; import  javax.servlet.http.HttpServletResponse; import  org.apache.struts.action.ActionForm; public   interface  ExcelDownLoad  {        /**     * 初始化要生成的Excel的表模型     * @param list List 填充了 Excel表格数据的集合     * @param form ActionForm及其子类     * @param excel ExcelModel Excel表的对象模型     * @see ExcelModel     * @throws Exception     */    public ExcelModel createDownLoadExcel (List list, ActionForm form,             ExcelModel excel)throws Exception;        /**     * 在已文件已存在的情况下,采用读取文件流的方式实现左键点击下载功能,     * 本系统没有采取这个方法,而是直接将数据传往输出流,效率更高。     * @param inPutFileName 读出的文件名     * @param outPutFileName 保存的文件名     * @param HttpServletResponse          * @see HttpServletResponse     * @throws IOException     */    public void downLoad(String inPutFileName, String outPutFileName,            HttpServletResponse response) throws IOException ;        /**     * 在已文件不存在的情况下,采用生成输出流的方式实现左键点击下载功能。     * @param outPutFileName 保存的文件名     * @param out ServletOutputStream对象         * @param downExcel 填充了数据的ExcelModel     * @param HttpServletResponse          * @see HttpServletResponse     * @throws Exception     */    public void downLoad(String outPutFileName, ExcelModel downExcel,            HttpServletResponse response) throws Exception ;}

             三、编写一个实现了以上接口的公共基类BaseExcelDownLoad,并提供downLoad()方法的公共实现,代码如下:

    /** * * <p>Title: BaseExcelDownLoad</p> * * <p>Description:Excel表下载操作基类,生成excel格式的文件流供下载 </p> * * <p>Copyright: Copyright (c) 2005-10-27</p> * * <p>Company: *** </p> * * @author zzj * @version 1.0 */ package  com.qnjian.myprj.excel; import  java.io.FileInputStream; import  java.io.IOException; import  java.io.InputStream; import  java.io.OutputStream; import  java.util.List; import  javax.servlet.http.HttpServletResponse; import  org.apache.struts.action.ActionForm; public   abstract   class  BaseExcelDownLoad  implements  ExcelDownLoad {             /**     * 初始化要生成的Excel的表模型     * @param list List 填充了 Excel表格数据的集合     * @param form ActionForm及其子类     * @param excel ExcelModel Excel表的对象模型     * @see ExcelModel     * @throws Exception     */    public abstract ExcelModel createDownLoadExcel (List list, ActionForm form,             ExcelModel excel)throws Exception;        /**     * 在已文件已存在的情况下,采用读取文件流的方式实现左键点击下载功能,     * 本系统没有采取这个方法,而是直接将数据传往输出流,效率更高。     * @param inPutFileName 读出的文件名     * @param outPutFileName 保存的文件名     * @param HttpServletResponse          * @see HttpServletResponse     * @throws IOException     */    public void downLoad(String inPutFileName, String outPutFileName,            HttpServletResponse response) throws IOException {                       //打开指定文件的流信息        InputStream is = new FileInputStream(inPutFileName);        //写出流信息         int data = -1;         OutputStream outputstream = response.getOutputStream();                 //清空输出流           response.reset();                      //设置响应头和下载保存的文件名                       response.setHeader("content-disposition","attachment;filename="+outPutFileName);         //定义输出类型         response.setContentType("APPLICATION/msexcel");                 while ( (data = is.read()) != -1)outputstream.write(data);        is.close();        outputstream.close();        response.flushBuffer();        }        /**     * 在文件不存在的情况下,采用生成输出流的方式实现左键点击下载功能。     * @param outPutFileName 保存的文件名     * @param out ServletOutputStream对象         * @param downExcel 填充了数据的ExcelModel     * @param HttpServletResponse          * @see HttpServletResponse     * @throws Exception     */    public void downLoad(String outPutFileName, ExcelModel downExcel,            HttpServletResponse response) throws Exception {               //取得输出流        OutputStream out = response.getOutputStream();        //清空输出流        response.reset();                //设置响应头和下载保存的文件名                      response.setHeader("content-disposition","attachment;filename="+outPutFileName);        //定义输出类型        response.setContentType("APPLICATION/msexcel");                         ExcelOperator op = new ExcelOperator();        //out:传入的输出流        op.WriteExcel( downExcel,out);        out.close();                //这一行非常关键,否则在实际中有可能出现莫名其妙的问题!!!       response.flushBuffer();//强行将响应缓存中的内容发送到目的地                                          }}

      请注意,BaseExcelDownLoad只是提供了一个生成下载文件流的公共方法,而Excel表格需要显示的数据内容则因具体业务与需求的不同而不同,因此,必须根据具体情况提供一个该类的继承类,实现createDownLoadExcel()方法,以生成所需要输出内容的Excel表格文件流。要说明的是,该方法的参数list ,实际上是一个ArrayList集合,我们将从数据库查询出来的记录集保存在其中,我想这是很容易做到的,实现的方式也可以各种各样。我项目中是通过Hibernate的Query对象取得查询结果集,它正好也是一个ArrayList。不同的客户,甚至不同功能模块内需要生成的Excel报表的内容都是不一样的。下面还是给出我的一个实现类作为例子吧。  四、实现按照具体的需求生成Excel表格文件流的类举例:继承自BaseExcelDownLoad类的AgentInfoExcelDownLoad:

    package  com.qnjian.myprj.excel; import  java.math.BigDecimal; import  java.util.ArrayList; import  java.util.Iterator; import  java.util.List; import  org.apache.struts.action.ActionForm; import  com.netease.bid.cs.model.BidAgent; import  com.netease.bid.cs.model.BidUser; import  com.netease.bid.cs.util.DateUtil; import  com.netease.bidding.cs.servlet.InitCsServlet; import  com.netease.bidding.cs.vo.InstructUser; public   class  AgentInfoExcelDownLoad  extends  BaseExcelDownLoad  {    public ExcelModel createDownLoadExcel(List list, ActionForm form,            ExcelModel excel) throws Exception {                String titleStr = "客户帐号;公司名称;所属地区;帐户余额;注册日期;联系方式;联系人;";            ArrayList data = new ArrayList();                        Iterator ir = list.iterator();        while(ir.hasNext()){                        ArrayList rowData = new ArrayList();                        BidAgent user = (BidAgent)ir.next();                    rowData.add(user.getName());            rowData.add(user.getCorpName());                                //取得所在省名称            String provStr = (user.getProvince()==null ) ?                     "" : InitCsServlet.getProvinceStr(                    user.getProvince());                        //取得所在地区名称             String cityStr = (user.getCity()==null?                     "" : InitCsServlet.getCityStr(                   user.getCity());            if(provStr.equals(cityStr)){                cityStr = "";            }            rowData.add(provStr+" "+cityStr);                                    BigDecimal balance =user.getReturnBalance().add(user.getBalance());            rowData.add(balance);                                    String date = DateUtil.getFormatDate(user.getCreateTime(),"yyyy-MM-dd");            rowData.add(date);                        rowData.add(user.getPhone());            rowData.add(user.getLinkMan());            data.add(rowData);                            }                    String[] titles = titleStr.split(";");                /*for(int i=0;i<titles.length;i++){            System.out.print(titles[i]+" ");        }*/                ArrayList header = new ArrayList();        for (int i=0;i<titles.length;i++){            header.add(titles[i]);        }                //设置报表标题        excel.setHeader(header);        //设置报表内容        excel.setData(data);        return excel;    }}

            五、编写一个操作类,进行生成下载文件流的操作:

    /** * * <p>Title: Excel表操作</p> * * <p>Description:用于生成Excel格式文件 </p> * * <p>Copyright: Copyright (c) 2005-10-20</p> * * <p>Company: *** </p> * * @author zzj * @version 1.0 */ package  com.qnjian.myprj.excel; import  org.apache.poi.hssf.usermodel. * ; import  java.io.FileOutputStream; import  java.io.BufferedOutputStream; import  java.util.ArrayList; import  java.math.BigDecimal; import  java.io.OutputStream; /** *实现生成Excel文件的操作 */ public   class  ExcelOperator {          /**       * 将数据信息写入到Excel表文件,采取自建输出流的方式。       * @param excel ExcelModel Excel表的模型对象           * @throws Exception       */     public  void WriteExcel(ExcelModel excel)throws Exception{               try{                 String file = excel.getPath();              //新建一输出文件流       FileOutputStream fOut = new FileOutputStream(file);       BufferedOutputStream bf =new BufferedOutputStream(fOut);                HSSFWorkbook workbook =this.getInitWorkbook(excel);                // 把相应的Excel 工作簿存盘        workbook.write(fOut);        fOut.flush();        bf.flush();        // 操作结束,关闭文件         bf.close();        fOut.close();       // System.out.println("Done!");      }catch(Exception e){        //System.out.print("Failed!");          throw new Exception(e.getMessage());         }           }               /**      * 将数据信息写入到Excel表文件 ,采取传入输出流的方式。      * @param excel Excel表的模型对象       * @param out  OutputStream 输出流      * @throws Exception      */     public  void WriteExcel(ExcelModel excel,OutputStream out)throws Exception{         try{             HSSFWorkbook workbook =this.getInitWorkbook(excel);             workbook.write(out);                          out.close();            // System.out.println("Done!");         }catch(Exception e){             //System.out.println("Failed!");             throw new Exception(e.getMessage());         }                       }        /**     * 取得填充了数据的工作簿     * @param excel ExcelModel Excel表的模型对象     * @return HSSFWorkbook 工作簿对象     */    private  HSSFWorkbook getInitWorkbook(ExcelModel excel){                        // 创建新的Excel 工作簿            HSSFWorkbook workbook = new HSSFWorkbook();             //在Excel工作簿中建一工作表             HSSFSheet sheet = null;             String sheetName = excel.getSheetName();                          if(sheetName!=null)sheet=workbook.createSheet(sheetName);             else sheet=workbook.createSheet();             //设置表头字体             HSSFFont font_h = workbook.createFont();             font_h.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);             //设置格式             HSSFCellStyle cellStyle= workbook.createCellStyle();             cellStyle.setFont(font_h);                          //在索引0的位置创建行(最顶端的行)               HSSFRow row = sheet.createRow((short)0);               ArrayList header = excel.getHeader();               if(header!=null){                   for(int i=0;i<header.size();i++){                  //在索引0的位置创建单元格(左上端)                  HSSFCell cell = row.createCell((short)i);                  // 定义单元格为字符串类型                 cell.setCellType(HSSFCell.CELL_TYPE_STRING);                 //设置解码方式                 cell.setEncoding((short)1);                 //设置单元格的格式                 cell.setCellStyle(cellStyle);                 // 在单元格中写入表头信息                 cell.setCellValue((String)header.get(i));                   }               }               ArrayList cdata = excel.getData();               for (int i=0;i<cdata.size();i++){                //从第二行开始                HSSFRow row1 = sheet.createRow(i+1);                ArrayList rdata =(ArrayList)cdata.get(i);               //打印一行数据                for (int j=0;j<rdata.size();j++){                    HSSFCell cell = row1.createCell( (short)j);                    cell.setCellType(HSSFCell.CELL_TYPE_STRING);                    //设置字符编码方式                    cell.setEncoding((short)1);                                       Object o = rdata.get(j);                                        //造型,使写入到表中的数值型对象恢复为数值型,                   //这样就可以进行运算了                    if(o instanceof BigDecimal){                        BigDecimal b=(BigDecimal)o;                        cell.setCellValue(b.doubleValue());                    }                    else if(o instanceof Integer){                           Integer it =(Integer)o;                           cell.setCellValue(it.intValue());                                               }                    else if(o instanceof Long){                        Long l =(Long)o;                        cell.setCellValue(l.intValue());                                            }                    else if(o instanceof Double){                        Double d =(Double)o;                        cell.setCellValue(d.doubleValue());                    }                    else if(o instanceof Float){                        Float f = (Float)o;                        cell.setCellValue(f.floatValue());                    }                    else{                        cell.setCellValue(o+"");                    }                                                             }               }           return workbook;                    }  /**   * Just to test   * @param args String[]   */  public static void main(String[] args){                ArrayList data=new ArrayList();                ArrayList header = new ArrayList();                header.add("学号");                header.add("姓名");                header.add("成绩");                for (int i=0;i<3;i++){                     ArrayList data1=new ArrayList();                            data1.add((i+1)+"");                            data1.add("Name"+(i+1));                            data1.add(""+(80+i));                            data.add(data1);                }                ExcelModel model = new ExcelModel();                model.setPath("E:/test.xls");                model.setHeader(header);                model.setData(data);                ExcelOperator eo= new ExcelOperator();               try{                  eo.WriteExcel(model);               }catch(Exception e){                     System.out.println(e.getMessage());               }        }}

             六、功能流程小结:         涉及到不同的项目,采取的框架与结构是可能不同的。我的实现方法可以应用到不同的项目中去,只是作为一个借鉴,它可能需要针对不同情况做相应调整与修改。         我的项目是采取了Spring+Struts+Hibernate的框架实现的,显示层仍然使用HTML、JSP文件,通过它传递客户端的请求,转到Action类调用业务逻辑对象实现相应功能。持久层使用了Hibernate,使用Hibernate作为数据持久层,在开发与维护方面都带来了较大的便利。至于相关框架的配置与实现,则不在本文论述的范围,请读者朋友参阅相关资料。我的项目中,使用了一个Service类(类似Manager的功能,编写接口与实现类,在Spring配置文件中加入,利用Spring的依赖注入技术在运行中取得对应的Bean实例......)来集成业务逻辑功能,通过它调用涉及到的数据访问类(DAO类),具体Dao类又利用Hibernate提供的对象进行数据库的查询或其他操作。这些东西,我就不再详述了,相信对这些技术使用得比我娴熟者大有人在。功能的最终实现,请看我在某个Action中的几行代码:

     ExcelModel excel  =   new  ExcelModel();                         excel.setSheetName( " BidCost " );                                                                                         // 写入到Excel格式输出流供下载                             try {                                                                                    //调用自编的下载类,实现Excel文件的下载                               ExcelDownLoad downLoad = new BidCostExcelDownLoad();                               ExcelModel downExcel= downLoad.createDownLoadExcel(bidReportList,bcf,excel);                               //不生成文件,直接生成文件输出流供下载                               downLoad.downLoad("BidCost.xls",downExcel,response);                                                              log.info("create Excel outputStream successful!");                                                                                     } catch (Exception e) {                               msg.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(                                       "bidding.cs.fileUpDown.fileDownError"));//文件下载失败!                                  saveErrors(request, msg);                                                                                               log.info("create Excel outputStream  failed!");                              log.info(e.getMessage());                              //e.printStackTrace();                                                            return mapping.getInputForward();                                                        }                        

             请看客户端的显示页面:            (声明:此文为原创,转载请注明出处及链接网址!)Last modified on 2006年4月15日 13:37:17

     


    最新回复(0)