您的位置:首页 > 其它

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

2015-07-08 22:33 288 查看
在线实时生成Excel文件流供下载
  我做过的一个项目,需要实现在线实时生成 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();




}



请看客户端的显示页面:

         


 

  转自 http://www.blogjava.net/qnjian/archive/2005/12/30/25999.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: