最近的一个项目比较忙,一直没时间过来跟新博客。今天过来分享一下在此项目中遇到的一个小问题:导出Excel;相信导出Excel这个功能是特别常见的,也有很多的方式。好了,不多说了,直接说说自己遇到的各种坑,我后台用的是poi导出。
首先,说一下需求,想要的结果就是:“符合导出条件时,正常导出,并弹出提示框让用户选下载地址,不符合导出条件时,提示用户不能导出,还要在用户导出的同时将导出按钮禁用,当导出成功后,再将导出按钮放开“
(我首先会将我做的全过程的思路和所遇到的问题和解决方法说一下,干货会在下面贴出来)
1、因为我们整个框架习惯用异步请求,所以一开始我使用ajax异步请求导出Excel,结果发现导出不了,后台程序也跑完了,日志也记录了,,就是没有出现excel,更别说提示框了,网上查了很多,都说是因为ajax和后台交流用的是字符流,浏览器是不会识别你让他下载的,所以此路不通。(也有小伙伴说用ajax同步,也试了,不行。又填上了一个坑。。。。)
2、然后就准备用form表单submit提交导出,试了一下,发现可以啊,很高兴,但是问题又来了,form表单提交后页面其实刷新了,如果不符合导出条件我该怎么提示用户呢?新跳转一个页面吗?感觉这样不太合适,因为用户辛辛苦苦输入了半天的条件,然后等了半天后台校验(涉及到大数据量),结果等来的是跑到另一个页面跟你说不能导出,如果是我我会崩溃的,等了半天不说,奶奶个腿,我输入半天的查询条件还得重新输入,这个不能忍。。好吧,这种方法不行,然后试着从后台往jsp页面写脚本,但是也发现根本不执行,也就纳闷了,最后,网上有一位小伙伴说用一个iframe的隐藏域,如果不符合条件的话,再用流的方式将问题写回来,最终问题解决了(代码将会统一贴在下面)。
3、这样感觉很完美了,但是问题还没有完全解决,还有一个禁用按钮的问题,这个问题想想很简单,用户点击导出时将按钮禁用,导出完成后传回来一个标志,然后再将按钮放开,但是问题来了,这个标志怎么穿回来?一开始我想当然的想和错误提示一样的结局方案,发现不行因为,我导出Excel已经将response流给用掉了,我不可能在用流的方式将标志写回来了。这样就会导致我放不开导出按钮,那老板就不同意了,加班加点的找方法。终于,让我找到了一个解决思路,用cookie来做。具体思路就是:用户点击导出后,禁用按钮,然后js写个定时器轮训找cookie,在将文件流写出后,我会放一个cookie在浏览器中,那么此时js就能找到这个cookie了,找到后,首先将定时器给干掉,再将cookie给干掉(有点过河拆桥的感觉,哈哈),然后将导出按钮给放开。
(虽然问题解决了,但是cookie和定时器轮训还是有一点不靠谱,希望大家有更好的解决方法,请大家不吝赐教,谢谢!!!)
前台代码:
<button class="btn btn-sm btn-success" type="submit" id="detailEp" onclick="return exportCheck(true);" forbid="yes"> <i class="icon-arrow-right bigger-110"></i> 导出 </button> <script> $(function() { var timer = ""; }); //点击导出按钮时禁用导出按钮 $("#myForm").submit(function() { $("button[type=submit]",this).attr("disabled","disabled"); //提交导出后定时去查看有没有导出成功 timer = setInterval(refrashPg,1000); }) //导出成功后会放开导出按钮的禁用 function refrashPg() { if (getCk() =="1") { clearInterval(timer); $("#detailEp").removeAttr("disabled"); } delCk(); } //js获取到cookie function getCk() { debugger var ck = document.cookie.split(";"); var ckname = ""; for (var i = 0;i<ck.length;i++) { var arr = ck[i].split("="); if (arr[0] =="updtstatus") { ckname = arr[1]; break; } } return ckname; } //js删除掉cookie function delCk() { var exp = new Date(); var name = "updtstatus"; exp.setTime(exp.getTime()-1000); var cval = getCk(); document.cookie = name + "=" + cval + "; expires=" + exp.toGMTString(); } }
</script>
后台导出代码就是普通的Java POI代码:
1 public void buildExcelDocument(Map<String, Object> obj,String fileName,String type, 2 HttpServletRequest request, HttpServletResponse response) 3 throws Exception { 4 HSSFWorkbook workbook = new HSSFWorkbook(); 5 if ("1".equals(type)) { 6 List<Map<String, Object>> contentList = (List<Map<String, Object>>) obj.get("content"); 7 HSSFSheet sheet = workbook.createSheet("sheet1");//创建Excel的版本是2003-2007(xls) 如果需要2010的话,用 XSSFSheet 8 Map<String,String> titleList = (Map<String, String>) obj.get("title"); 9 //表头的key 对应内容listMap中的map的key 10 List<String> mkey = new ArrayList<String>(); 11 //表头的value 表头 12 List<String> mvalue = new ArrayList<String>(); 13 Iterator<Entry<String, String>> it = titleList.entrySet().iterator(); 14 while(it.hasNext()){ 15 @SuppressWarnings("rawtypes") 16 java.util.Map.Entry entry = (java.util.Map.Entry)it.next(); 17 mkey.add((String) entry.getKey()); 18 mvalue.add((String) entry.getValue()); 19 } 20 sheet.setDefaultColumnWidth((short) 12); 21 HSSFCell cell = null; 22 for (int i = 0;i<mvalue.size();i++) { 23 cell = getCell(sheet, 0, i); 24 setText(cell, mvalue.get(i)); 25 } 26 27 for (short i = 0; i < contentList.size(); i++) { 28 HSSFRow sheetRow = sheet.createRow(i+1); 29 Map<String, Object> entity = contentList.get(i); 30 for (int j = 0;j< mkey.size();j++) { 31 if (entity.get(mkey.get(j)) instanceof String) { 32 sheetRow.createCell(j).setCellValue((String)entity.get(mkey.get(j))); 33 } else if (entity.get(mkey.get(j)) instanceof Double) { 34 sheetRow.createCell(j).setCellValue((Double)entity.get(mkey.get(j))); 35 } else if (entity.get(mkey.get(j)) instanceof Date){ 36 String date = (entity.get(mkey.get(j))).toString(); 37 sheetRow.createCell(j).setCellValue(date.substring(0, 10)); 38 } else if (entity.get(mkey.get(j)) instanceof BigDecimal){ 39 sheetRow.createCell(j).setCellValue((entity.get(mkey.get(j))).toString()); 40 } 41 } 42 } 43 } 44 } else if ("2".equals(type)) { 45 List<String[]> contentList = (List<String[]>) obj.get("content"); 46 HSSFSheet sheet = workbook.createSheet("sheet1"); 47 String[] titleList = (String[]) obj.get("title"); 48 HSSFCell cell = null; 49 for (int i = 0;i<titleList.length;i++) { 50 cell = getCell(sheet, 0, i); 51 setText(cell, titleList[i]); 52 } 53 for (short i = 0; i < contentList.size(); i++) { 54 HSSFRow sheetRow = sheet.createRow(i+1); 55 String[] detail = contentList.get(i); 56 for (int j = 0;j< detail.length;j++) { 57 sheetRow.createCell(j).setCellValue((String)contentList.get(i)[j]); 58 } 59 } 60 } 61 62 //设置下载时客户端Excel的名称 63 String filename = fileName + ".xls"; 64 //处理中文文件名 65 filename = Chineseutil.encodeFilename(filename, request); 66 response.setContentType("application/Vnd.ms-excel;charset=UTF-8"); 67 response.setHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes("gb2312"), "iso8859-1")); 68 OutputStream ouputStream = response.getOutputStream(); 69 workbook.write(ouputStream); 70 71 //导出数据后将信息存入cookie 72 Cookie status = new Cookie("updtstatus", "1"); 73 status.setMaxAge(3600);//设置cookie存活时间1个小时 74 response.addCookie(status); 75 76 ouputStream.flush(); 77 ouputStream.close(); 78 }