最近在工作中遇到的一个问题,要将html标签里的Table跟echarts生成报表导出到excel里面,没有搞过各种谷歌!-_-,还好解决了,就分享一下这次踩坑的经验了.
所使用的jar包:
commons-codec-1.9.jar commons-lang3-3.3.2.jar jsoup-1.8.3.jar poi-3.9.jar slf4j-api-1.7.7.jar slf4j-simple-1.7.7.jar table-to-xls-0.0.1-RELEASE.jar//这个可以在oscchina的git上找到的谢谢大神的代码了
首先说一说实现的思路:
前端用jquery获取Table标签的内容跟echarts报表的图片的base64----->2.ajax的post传输数据到后台----->3将获取到的数据分别处理,表格生成excel再将base64还原成图片插入到生成的excel里面
具体实现:
第一步:
首先说明一下echarts在生成图表之前都要先初始化一个东西所以base64可以根据这个来获取
var showCharts=[];//这是个全局变量用来接收每个chart对象的 //在每个chart初始化的时候将初始化的对象放进数组 var myChart = echarts.init(document.getElementById(tagid)); showCharts.push(myChart);
第二步:通过页面上的一个按钮触发一个函数(click事件)
function export2Excel(tableId){ var tableContext =$("#"+tableId).html(); var datas={} if(tableContext.length>37){//这里为什么判断长度大于37因为表格动态生成的默认长度就有36!!:) tableContext="<table>"+tableContext+"</table>"; datas.tableContext=tableContext; } if(showCharts.length!=0){ var imgUrls = []; for(var x =0; x<showCharts.length;x++){ imgUrls.push(showCharts[x].getDataURL("png")); } datas.imgUrls=imgUrls; } if(datas.tableContext){ $.ajax({ url:url,//发送到后台处理这些数据的控制器地址(这个只是负责发送数据到后台) type: "POST", dataType: "json", data: $.param(datas,true),//发送数组这样的数据格式必用这个东西不然很可能出问题 success: function (result) { if(parseInt(result.status)==0){ location.href=url+fileName//用来处理下载请求的控制器地址跟下载的文件名,为啥要有这一步呢因为ajax不支持下载的浏览器是弹不出那个保存文件的框的(有没有草泥马) } }, failure: function (err) { alert("ajax产生了错误!"); } }); }else{ alert("必须有表格或者图表才能下载"); }
第三步:后台处理
//这个控制器是用来预处理ajax的post请求提交过来的数据的 @RequestMapping(value = "preExportToExcel",method=RequestMethod.POST) @ResponseBody public AjaxData<Map<String,String>> preExportToExcel(String tableContext, String[] imgUrls,HttpServletRequest req,HttpServletResponse rep) throws Exception { AjaxData<Map<String,String>> ajax = new AjaxData<Map<String,String>>(); tableContext=HtmlUtils.htmlUnescape(tableContext); String filePath=req.getSession().getServletContext().getRealPath("/")+"temp"; filePath = filePath.replace("\\","/")+"/"; HtmlTableToExcel html = new HtmlTableToExcel(imgUrls, filePath, tableContext);//这个对象会将传递过来的数据生成excel String xlsFileName = html.mergePicAndExcel(); Map<String,String> map = new HashMap<String,String>(); map.put("xlsFileName", xlsFileName); ajax.setData(map); return ajax; } //这个控制器才是真正的下载控制器对应ajax里面的location.href那句代码 //采用servlet这种方式下载的原因就是可以在下载完成的时候可以将文件删掉 @RequestMapping(value = "exportToExcel",method=RequestMethod.GET) @ResponseBody public void exportToExcel(String xlsFileName, HttpServletResponse rep) throws Exception { OutputStream out = null; try { rep.reset(); rep.setContentType("application/vnd.ms-excel"); rep.setHeader("Content-Disposition","attachment; filename=" + xlsFileName.substring(xlsFileName.lastIndexOf("/") + 1)); out = rep.getOutputStream(); System.out.println(xlsFileName); out.write(FileUtils.readFileToByteArray(new File(xlsFileName))); out.flush(); File file = new File(xlsFileName.substring(0,xlsFileName.lastIndexOf("/"))); File[] files= file.listFiles(); for (File file2 : files) { FileUtils.deleteFile(file2.getAbsolutePath()); } } catch (IOException e) { throw new RuntimeException(e.getMessage() + "文件下载失败"); } finally { if (out != null) { try { out.close(); } catch (IOException e) { throw new RuntimeException(e.getMessage() + "文件下载失败"); } } } }
所用类的代码:
值得注意的是图片的后缀名必须是.png结尾的额不然透明背景的图片插入到excel里面是要变成黑乎乎的
/** * 将图片的base64码解密并还原图片(只支持支持格式"png","jpg",支持多张图片) * */ public class Base64ToImage { /** * Logger for this class */ private static final Logger logger = Logger.getLogger(Base64ToImage.class); /** * 枚举类用来选择模式 * */ public static enum PictureType { //png的图片类型 TYPE_PNG,TYPE_JPG; } /** * * @param imgUrls 多张图片的base64码组 * @param filePath 保存文件的路径 * @param type 生成文件的模式 * @return 返回生成的图片的名字 */ public static List<String> base64ToPicture(String[] imgUrls,String filePath,Base64ToImage.PictureType type){ String suffix=""; //判断传入的图片类型 if(type==PictureType.TYPE_PNG) suffix=suffix+".png"; if(type==PictureType.TYPE_JPG) suffix=suffix+".jpg"; //判断空值以及0值 if(imgUrls.length==0||imgUrls==null||StringUtils.isBlank(filePath)){ return null; } List<String> fileNameList= new ArrayList<String>(); for (String imgUrl : imgUrls) { String fileName = UUID.randomUUID().toString(); String realFileName=filePath+fileName; if(isCreatePic(imgUrl, realFileName,suffix)) fileNameList.add(fileName+suffix); logger.info(fileName); } return fileNameList; } /** * * @param imgsURl 图片的base64码 * @param fileName 图片名称 * @return 是否生成图片成功 */ private static boolean isCreatePic(String imgsURl, String fileName,String suffix){ BASE64Decoder decoder = new BASE64Decoder(); boolean flag = true; OutputStream out=null; try { String[] url = imgsURl.split(","); String u = url[1]; // Base64解码 byte[] buffer = new BASE64Decoder().decodeBuffer(u); // 生成图片 out= new FileOutputStream(new File(fileName + suffix)); out.write(buffer); out.flush(); } catch (Exception e) { flag = false; }finally{ try { if(out!=null)out.close(); } catch (IOException e) { flag=false; } } return flag; } } // 将页面上的html 表格导出(支持页面上的报表图片导出,只支持单表的导出,只支持png格式图片插入) public class HtmlTableToExcel { private static final Logger logger = Logger.getLogger(HtmlTableToExcel.class); //图片的base64码 private String[] imgUrls; //存放生成文件的路径 private String filePath; //生成的文件名字 // private String fileName=UUID.randomUUID().toString(); private String tableContext; public HtmlTableToExcel(String[] imgUrls,String filePath, String tableContext) { this.imgUrls = imgUrls; this.tableContext = tableContext; this.filePath=filePath; } public String createExcel(){ FileOutputStream fout=null; try { String xlsFileName =this.filePath+UUID.randomUUID().toString()+".xls"; fout = new FileOutputStream(xlsFileName); TableToXls.process(tableContext, fout); return xlsFileName; } catch (FileNotFoundException e) { e.printStackTrace(); }finally{ try { if(fout!=null)fout.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } public String mergePicAndExcel(){ String xlsFileName=createExcel(); if(imgUrls!=null){ List<String>fileNames = Base64ToImage.base64ToPicture(this.imgUrls, this.filePath, PictureType.TYPE_PNG); for(int x = 0;x < fileNames.size();x++){ drawPiceToExcel(xlsFileName, this.filePath+fileNames.get(x),x); } } return xlsFileName; } private void drawPiceToExcel(String xlsFileName,String picFileName,int position){ FileOutputStream fileOut = null; BufferedImage bufferImg = null; FileInputStream in = null; Workbook wb = null; try { // 先把读进来的图片放到一个ByteArrayOutputStream中,以便产生ByteArray ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); bufferImg = ImageIO.read(new File(picFileName)); ImageIO.write(bufferImg, "png", byteArrayOut); // 打开一个工作薄 in = new FileInputStream(xlsFileName); POIFSFileSystem fs = new POIFSFileSystem(in); wb = new HSSFWorkbook(fs); HSSFSheet sheet = (HSSFSheet) wb.getSheetAt(0); HSSFPatriarch patriarch = sheet.createDrawingPatriarch(); HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 1023, 100, (short) 0, sheet.getLastRowNum()+5*position+27*position, (short) (sheet.getRow(0).getPhysicalNumberOfCells()+8), sheet.getLastRowNum()+5*position+27*(position+1)); // 插入图片 patriarch.createPicture(anchor,wb.addPicture(byteArrayOut.toByteArray(),HSSFWorkbook.PICTURE_TYPE_JPEG)); fileOut = new FileOutputStream(xlsFileName); // 写入excel文件 wb.write(fileOut); fileOut.close(); } catch (IOException io) { throw new RuntimeException(io.getMessage()+"读取文件出问题"); } finally { if (fileOut != null) { try { fileOut.close(); } catch (IOException e) { e.printStackTrace(); } } } } }