Ajax提交无法导出Excel

Ajax提交无法导出Excel

这几天在做一个报表的导出,用的是Ajax方式提交的,一直下载不了附件。。。

后来在网上找到了一个解决方案,用Iframe的方式进行提交。。。

源代码如下:
js代码:
//导出报表
function toExport(name, gridcontainer){
if(confirm("确定要导出报表数据?")){
var settMonth=$('#settleMonth').val();
if(settMonth ==''){
alert("请选择帐期!");
return;
}
if (!checkAtLeastOne(gridcontainer.grid.getSelectedRows(), '操作!')) {
return false;
}
var values = gridcontainer.grid.getSelectedCellValues(['REPORT_ID']);
var data=serializeStringWithEncode(values,['REPORT_ID'], ['reportIds']);
var resutId='';
if(data.indexOf("&") > 0){
var arry=new Array();
arry=data.split("&");
for(var i=0;i<arry.length;i++){
var arryObj=arry[i];
if(i < arry.length -1){
resutId=resutId+arryObj.split("=")[1]+"_";
}else{
resutId=resutId+arryObj.split("=")[1];
}
}
}else{
resutId=data.split("=")[1];
}

var url = contextPath+"/localnet/comexport/generExport/exportExcelMethod.action?reportId="+resutId+"&settleMonth="+settMonth+"&random="+Math.random();
var iframe = document.createElement("iframe");
iframe.src = url;
var imgPath=contextPath+"/base/css/images/default/shared/blue-loading.gif";
$.blockUI({
message:"<img src='"+imgPath+"' /><h4>[b]报表正在导出中,请稍后....[/b]</h4>",
css:{background:'none',color: '#000',border:'none'},
overlayCSS:{backgroundColor:'#C5E1F0',opacity:'0.4'}
});

if (!/*@cc_on!@*/0) { //if not IE
iframe.onload = function(){
$.unblockUI();
};
} else {
iframe.onreadystatechange = function(){
//文件下载是在http请求的interactive也就是浏览器交互阶段。
if (iframe.readyState == "interactive"){
$.unblockUI();
}
};
}

iframe.style.display = 'none';
document.body.appendChild(iframe);

}

}


Java代码:

public String exportExcelMethod() throws Exception{
System.out.println("账期:"+settleMonth +", 报表ID:reportId="+reportId);
initReportIds(reportId);

ServiceResult<RReportDefBean> context = super.getContext();
//压缩文件路径
String zipPath=File.separator + pathOut + File.separator + settleMonth;
//压缩包存放路径
//String zipPakPath=File.separator + pathOut + File.separator + settleMonth+ File.separator;
String zipPakPath=File.separator + pathOut + File.separator;

//获取业务名称,用于导出zip包,这个业务名称应该是业务类型的上级名称
String expName="报表数据";

//List<RReportDefBean> reportDefBeans=reportDefService.getReportDefList(reportDefBean);
//模板路径
//输出路径为/ppmWeb/outtemplet/报表编码/账期
String encoding = System.getProperty("sun.jnu.encoding");

if(reportIds.length>0){
for (int i = 0; i < reportIds.length; i++) {
//读取文件模板到输出目录下
RReportDefBean defBean=new RReportDefBean();
defBean.setReportId(reportIds[i]);
defBean= reportDefService.loadEntity(defBean);
String srcReportName=defBean.getReportName()+".xls";
String desRepotName=defBean.getReportName()+"_"+settleMonth+".xls";

RReportBusinessDefBean businessDefBean =new RReportBusinessDefBean();
businessDefBean.setBusinessCode(defBean.getBusinessCode());
businessDefBean=reportBusinessDefService.loadEntity(businessDefBean);

String srcPath=File.separator + pathIn + File.separator + defBean.getBusinessCode() + File.separator+ new String(srcReportName.getBytes("GBK"),encoding );
//目标模板目录用中文业务名
String outPath=File.separator + pathOut + File.separator + settleMonth + File.separator + new String(businessDefBean.getBusinessName().getBytes("GBK"),encoding );
File file=new File(ServletActionContext.getServletContext().getRealPath(srcPath));
System.out.println("模板的绝对路径:"+file.getAbsolutePath());
if(file.exists()){
//将模板复制目标目录
copyFileMethod(srcPath, outPath,desRepotName);
File desFile=new File(ServletActionContext.getServletContext().getRealPath(outPath + File.separator + new String(desRepotName.getBytes("GBK"),encoding)));
if(desFile.exists()){
//替换模板中的变量数据
FileInputStream fInputStream =new FileInputStream(desFile);
HSSFWorkbook hWorkbook=new HSSFWorkbook(fInputStream);
//替换模板中的变量
RReportInstPartTitleBean titleBean=new RReportInstPartTitleBean();
titleBean.setReportId(reportIds[i]);
titleBean.setSettleMonth(Long.valueOf(settleMonth));
ServiceResult<RReportInstPartTitleBean> serviceResult= reportInstPartTitleService.queryList(titleBean, new Limiter());
if(serviceResult !=null && serviceResult.getResult().size()>0){
List<RReportInstPartTitleBean> listBeans=serviceResult.getResult();
if(listBeans !=null && listBeans.size()>0){
for (RReportInstPartTitleBean titleBean2 : listBeans) {
long hCell= titleBean2.getHcell(); //纵坐标
long vCell= titleBean2.getVcell(); //横坐标
long sheetNo= titleBean2.getSheetNo();
long paramId= titleBean2.getParamId();
HSSFSheet sheet = hWorkbook.getSheetAt(new Long(sheetNo).intValue());
HSSFRow row = sheet.getRow(new Long(vCell).intValue() -1);
if(row ==null){
row = sheet.createRow(new Long(vCell).intValue()-1);
}
HSSFCell cell = row.getCell(new Long(hCell).intValue()-1);
if(cell ==null){
cell = row.createCell(new Long(hCell).intValue()-1);
}
String paraStr= getChargeParam(settleMonth,titleBean2.getParamId());
String cellOld = cell.getStringCellValue();
if(cellOld !=null && !"".equals(cellOld)){
if(paramId ==1L){
cell.setCellValue(cellOld.replace("#JSZQ#","")+paraStr);
}
if(paramId ==2L){
cell.setCellValue(cellOld.replace("#TJRQ#","")+paraStr);
}
}else {
cell.setCellValue(paraStr);
}

}
}
}else {
System.out.println("报表ID:reportId, 账期:"+settleMonth+" ,对应的变量账期不存在!请核查R_REPORT_PARAM表的配置.......");
}

//写模板实例数据
//这个地方要区分SP的三个动态模板
String excepIds[]={"11010001","11010002","11010003"};
List<String> excepList = Arrays.asList(excepIds);


Map<String, Object> map=new HashMap<String, Object>();
List<ComReportInstBean> reportInstBeans=null;
if (excepList.contains(reportIds[i])) {
//这三个目前需特殊处理
map.put("reportId",Long.valueOf(reportIds[i]));
map.put("settleMonth", Long.valueOf(settleMonth));
reportInstBeans=comReportInstService.getReportExcepList(map);
System.out.println("报表ID="+reportIds[i] +",按照旧的表结构进行数据提取......");

}else {
map.put("fees", "fees"+settleMonth.substring(4, 6));
map.put("settleYear", Long.valueOf(settleMonth.substring(0, 4)));
map.put("reportId",Long.valueOf(reportIds[i]));
reportInstBeans=comReportInstService.getComReportInstList(map);
}

if(reportInstBeans!=null && reportInstBeans.size()>0)
{
for(ComReportInstBean instBean: reportInstBeans)
{
HSSFSheet hssfSheet=hWorkbook.getSheetAt(Integer.parseInt(instBean.getSheetno().toString()));
HSSFRow row=hssfSheet.getRow(Integer.parseInt(instBean.getVcell().toString())-1);
double fees=instBean.getFees(); //费用字段在bean中最好设置为double类型
//当模板有问题的时候,取到的列可能为空
if(instBean.getSheetno()==0L){
System.out.println("打印信息:::账期:"+settleMonth +"报表ID="+reportId +" ;sheet页="+instBean.getSheetno() +"; 行号="+instBean.getVcell() +" ;列号="+instBean.getHcell() +";fees="+fees);
}

HSSFCell cell=row.getCell(Integer.parseInt(instBean.getHcell().toString()) -1);
if(cell ==null)
{
cell=row.createCell(Integer.parseInt(instBean.getHcell().toString()) -1);
}else {
cell.setCellType(cell.getCellType());
}
//额度要除以100,库表中是以分为单位的,报表中以元为单位
if(fees==0.0){
cell.setCellValue(fees);
}else {
cell.setCellValue(fees / 100);
}
}

fInputStream.close();
FileOutputStream fOutputStream=new FileOutputStream(new File(ServletActionContext.getServletContext().getRealPath(outPath + File.separator +new String(desRepotName.getBytes("GBK"),encoding ))));
//强制Excle执行公式
hWorkbook.setForceFormulaRecalculation(true);
hWorkbook.write(fOutputStream);
fOutputStream.close();
}
else
{
System.out.println("报表ID为"+reportId+",账期为"+settleMonth +",的实例数据不存在.......");
}
}else {
System.out.println("模板复制失败.........");
}

}else {
System.out.println("模板:"+srcReportName+"在目录"+File.separator + pathIn + File.separator + defBean.getBusinessCode() + File.separator+"下不存在,请检查模板....");
}
}

//打包输出
String zipName = expName+ "_" + settleMonth +".zip";
//zipName = new String(zipName.getBytes("GBK"), "ISO8859-1");

String zipFileName =File.separator + zipPakPath + zipName;
System.out.println("zipFileName路径:"+ServletActionContext.getServletContext().getRealPath(zipFileName));
System.out.println("zipPath路径:"+ServletActionContext.getServletContext().getRealPath(zipPath));
String floder = zipPath;
//第一个参数是打包的文件夹路径, 第二个打包后的文件名(带路径)
ZipUtils.zip(ServletActionContext.getServletContext().getRealPath(floder), ServletActionContext.getServletContext().getRealPath(zipFileName));
outputReport(ServletActionContext.getServletContext().getRealPath(zipFileName),zipName);

}else {
System.out.println("传入的数组参数reportIds,businessCodes的长度为0,请检查参数!");
context.setSuccess(false);

}

return NONE;
}

****************************************************************************************

/*
* 复制文件
* srcPaht :原路径
* desPath :目标路径
*/
public void copyFileMethod(String srcPaht, String desPath,String fileName){
//目录路径可能不存在,则要新建
String encoding = System.getProperty("sun.jnu.encoding");
try {
File file=new File(ServletActionContext.getServletContext().getRealPath(desPath));
if(!file.exists() && !file.isDirectory()){
System.out.println("方法:copyFileMethod 重建目标模板目录......");
file.mkdirs();
}
//目标路径下的文件如果已存在,则要删除

File file2=new File(ServletActionContext.getServletContext().getRealPath(desPath + File.separator + new String(fileName.getBytes("GBK"),encoding)));
if(file2.exists()){
System.out.println("方法:copyFileMethod 删除已经存在的模板实例......");
file2.delete();
}
} catch (UnsupportedEncodingException e1) {
System.out.println("方法copyFileMethod: 删除已经存在的模板实例文件出错......");
e1.printStackTrace();
}

try {
FileInputStream in=new FileInputStream(ServletActionContext.getServletContext().getRealPath(srcPaht));
FileOutputStream out=new FileOutputStream(ServletActionContext.getServletContext().getRealPath(desPath+ File.separator + new String(fileName.getBytes("GBK"),encoding)));


FileChannel inChannel=null;
FileChannel outChannel=null;

inChannel=in.getChannel();
outChannel=out.getChannel();

inChannel.transferTo(0, inChannel.size(), outChannel);
} catch (FileNotFoundException e) {
System.out.println("方法copyFileMethod: 复制模板文件出错......");
e.printStackTrace();
} catch (IOException e) {
System.out.println("方法copyFileMethod: 复制模板文件出错......");
e.printStackTrace();
}
}

/*
*输出文件
*/
public void outputReport(String filePath,String fileName) throws Exception{
System.out.println("导出文件路径:"+filePath +"; 导出文件名:"+fileName);
System.out.println("转换后的导出文件名:"+new String(fileName.getBytes("GBK"), "UTF-8"));
OutputStream ostream = null;
FileInputStream istream = null;
try{
ostream=response.getOutputStream();
byte b[]=new byte[1024];
File downFile = new File(filePath);
//response.setHeader("Content-disposition","attachment;filename="+fileName);
response.setHeader("Content-disposition","attachment;filename="+new String(fileName.getBytes("GBK"), "ISO8859-1"));
response.setContentType("application/zip");
long fileLength=downFile.length();
//String length=String.valueOf(fileLength);
String length=Long.toString(fileLength);
response.setHeader("Content_Length",length);
istream=new FileInputStream(downFile);
int n=0;
while((n=istream.read(b))!=-1){
ostream.write(b,0,n);
}
}catch(Exception e){
e.printStackTrace();
throw e;
}finally{
if(istream!=null){
try {
istream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(ostream!=null){
try {
ostream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}



==================================================下面粘贴一些原理=====================================================
iframe的readyState

最开始让我去研究这个问题是因为最近在做Excel数据导出时碰到的一个问题。导出Excel的基本做法是请求servlet生成一个Excel下载。由于是请求了一个不可见的iframe,所以整个请求过程一直到提示文件下载,除了能看见进度条在跑之外页面几乎没有任何反应,所以我想是不是可以做一个简单的提示正在下载文件之类的。做这个提示的关键就在于捕捉iframe的状态。

最开始大家都会想到用iframe的onload事件去判断iframe是否加载完毕。代码可能会这么写

<iframe id="f" src="demo.docx" style="display:none" onload ="iframe_onload()" ></iframe>
但是事实上文件下载的iframe有别于contentType为text/plain的iframe。

在IE,Opera,Chrome下onload并没有执行iframe_onload。
在Firefox下可以执行。执行的顺序是先执行iframe_onload,然后再提示文件下载。

上面的小小挫折让我又想到了IE的onreadystatechange事件,代码写成这样:
<iframe id="f" src="demo.docx" style="display:none" border="0" onload ="iframe_onload()" onreadystatechange="iframe_readystatechange();"></iframe>
javascript方法:
function iframe_onload (){
alert("done.");
}
function iframe_readystatechange(){//IE works
alert(document.getElementById("f").readyState);//interactive [prompt download file] complete
}
有趣的是,IE执行的顺序是interavtive ,提示文件下载,complete。

有必要看看readyState的定义了。
readyState的五种状态详解

readyState有五种可能的值:
0 (未初始化): (XMLHttpRequest)对象已经创建,但还没有调用open()方法。
1 (载入):已经调用open() 方法,但尚未发送请求。
2 (载入完成): 请求已经发送完成。
3 (交互):可以接收到部分响应数据。
4 (完成):已经接收到了全部数据,并且连接已经关闭。

The state of the request. The five possible values are 0 = uninitialized, 1 = loading, 2 = loaded, 3 = interactive, and 4 = complete。

可以看出IE能捕捉到下载文件的iframe的interactive状态和complete状态。而且可以看出提示文件下载是在http请求的interactive也就是浏览器交互阶段。

如果把iframe中的src换成一个普通的URL。看到的提示是interactive->complete->loaded。这说明iframe的onload是在http请求的complete之后触发。而且在interactive和complete阶段,可以通过contentWindow.document访问到iframe中的DOM元素(当然,跨域还是不行的,跟ajax一样)。

在interactive阶段能访问到iframe的document。但是按照interactive的定义,正在处理相应数据,可以认为浏览器还在渲染请求到的HTML。渲染没有完毕,应该是不能访问的到iframe中的DOM元素的。

不管怎样,我倒是曾经利用onreadystate的方法改造过IE only的弹出iframe,大家知道用window.open弹出的窗口可以用window.close关闭的。但是如果弹出的iframe层也想用window.close()关闭,基本思路是在iframe的onload时重写iframe的close方法来关闭iframe。这样原来使用window.open打开的页面的代码就不需要修改了。实际上做的时候发现了这么个问题。就是如果window.open方法弹出的页面只有一个<script>window.close()</script>的话。iframe还是不能关闭。这时候可以在iframe的interactive就把iframe.close方法重写。这样,还是可以使用window.close方法关闭弹出层。

总之,利用iframe的onreadystatechange可以做很多的事情,遗憾的是,只有IE能做到这样的效果,其他浏览器iframe没有readyState属性。到目前为止,也没有发现能替代该属性的做法。由于不能兼容其他浏览器,请求隐藏iframe下载文件也变成了在新浏览器窗口下载Excel文件,但是这个过程加深了我对readyState的理解。


参考:http://www.blogjava.net/Hafeyang/archive/2010/12/12/readystate_property_of_iframe.html



@yanggaonanlu.pudongqu.shanghai 2014-07-12 0:23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值