echarts柱状图保存为图片导出到Excel中(详细过程)

最近项目中有一个需求,要把echarts堆叠柱状图保存为图片,传到后台再导出到Excel中

下面分几个步骤分享一下实现的过程以及遇到的问题。

首先,我用的poi是3.15版本的,echarts是最新的4.0版本(echarts应该是哪个版本都支持的)

<dependency>
		    <groupId>org.apache.poi</groupId>
		    <artifactId>poi</artifactId>
		    <version>3.15</version>
		</dependency>
		
		<dependency>
		    <groupId>org.apache.poi</groupId>
		    <artifactId>poi-ooxml</artifactId>
		    <version>3.15</version>
</dependency>
<script type="text/javascript" src="${staticPath}/www/echarts.min.js?t=${currentTimeMillis}"></script>

1.第一步:在页面画出echarts图表,下面代码仅供参考,详细查看ecahrts官网。代码里面关键的两点是

 (1)animation:false //设置option中的属性关闭动画,否则后面导出到Excel中的图片只有横纵坐标,没有数据

 (2)生成echarts图表后,return myChart.getDataURL(); 返回echarts图的base64编码,默认为png格式。

function queryCostStructure(reportYear,reportType){
	   			//基于准备好的dom,初始化echarts实例
	   		        var myChart = echarts.init(document.getElementById('main1left'));
	   		        // 指定图表的配置项和数据
	   		        var option = {
	   		            tooltip: {
	   		                 trigger: "axis",  
	   		            },
	   		            legend: {
	   		                itemWidth:15,  
	   		                itemHeight:15,  
	   		                data:['材料费','设备费','人员费'],
	   		            },
	   		        	grid: {
		   		        	top: '2%',       //柱状图距离父容器div顶端的距离
		   		            left: '2%',      //柱状图距离父容器div左端的距离
		   		            right: '2%',    //柱状图距离父容器div右端的距离
		   		            bottom: '0%',    //柱状图距离父容器div底端的距离
		   		            containLabel: true  //grid 区域是否包含坐标轴的刻度标签
			   		      },
	   		            xAxis: {
	   		                data: [],//横轴坐标
	   		                splitLine:{
	   		                    show:false,
	   		                },
	   		                axisLabel :{interval:0,rotate:50} //rotate表示横轴文字倾斜角度
	   		            },
	   		            yAxis: {
	   		                 splitLine:{
	   		                    show:false,
	   		                },
	   		            },
	   		            series: [{
	   		                name: '材料费',
	   		                type: 'bar',
	   		                stack:'费用构成',
	   		                data: [],
	   		            	barWidth : 20,//柱图宽度
	   		                itemStyle:{
	   		                     normal:{color:"#00BFFF"},
	   		                }
	   		            },{
	   		                name: '设备费',
	   		                type: 'bar',
	   		                stack:'费用构成',//表示在哪一列
	   		                data: [],
	   		                itemStyle:{
	   		                     normal:{color:"#40E0D0"},
	   		                }
	   		            },{
	   		                name: '人员费',
	   		                type: 'bar',
	   		                stack:'费用构成',//表示在哪一列
	   		                data: [],
	   		                itemStyle:{
	   		                     normal:{color:"#F4A460"},
	   		                }
	   		            }],
	   		           animation:false //关闭动画,否则导出到Excel是无数据的
	   		        };
	   				$.ajax({
	   		            type: "post",
	   		            url: "${adminPath}/budgetStatistic/projectResearchBudgetEcharts",
	   		            dataType:"json",
	   		            async:false,
	   		            data: {"reportYear":reportYear,
	   		            	   "reportType":reportType},
	   		            success:function(result){
	   		            }	
	   		        });
	   			    // 使用刚指定的配置项和数据显示图表。
	   		        myChart.setOption(option);
	   		 	
	   		        //获取echarts图的base64编码,为png格式。
	   		     	return myChart.getDataURL();
	   		}

2.第二步:将获取的echarts图表的base64编码传到后台

在传值时遇到了好多问题,最开始我用window.location.href直接向后台发请求,但是后台报错“java.lang.IllegalArgumentException: Request header is too large”,查询之后发现,window.location.href方法发送请求默认是用get方式,而echarts图表的base64编码比较大,get请求方式不支持。

想了半天,改用ajax传值,

$.post("${adminPath}/budgetStatistic/export/exportForEchartsImg",{'picBase64Info':picBase64Info},function(data){});

不报错了,后台也能接到值了,但是最后Excel下载的时候出问题了,后台没有报错,但是页面没有任何反应,下载路径里也没有下载的文件,这时的我很是上火! 又查了资料,发现ajax返回的是字符型数据,而此处我们需要的是返回文件流到浏览器,我们才能下载到文件,两者不兼容,这个请求方式也被pass掉。

最后方案,使用表单的方式发行请求,既满足可以使用post方式发请求,又不与下载后刷新页面冲突。

document.write("<form action='${adminPath}/budgetStatistic/export/exportForEchartsImg' method=post name='form1' style='display:none'>");  
				    document.write("<input type=hidden name='picBase64InfoCostStr' value='"+picBase64InfoCostStr+"'/>");  
				    document.write("<input type=hidden name='picBase64InfoCategory' value='"+picBase64InfoCategory+"'/>");  
				    document.write("<input type=hidden name='picBase64InfoExpectedGain' value='"+picBase64InfoExpectedGain+"'/>");
				    document.write("<input type=hidden name='picBase64InfoProjectType' value='"+picBase64InfoProjectType+"'/>");
				    document.write("</form>");  
				    document.form1.submit();

至此,前两步已经大功告成,获取到ecahrts的base64编码,并且发送给后台了。

3.第三部,后台解析base64编码,并导出到Excel中

我们拿到的数据样式大致是这样的

 data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABI4AAAEsCAYAAAClh/jbAAAgAElEQVR4XuzdB3hUVfo
 /8O+dSe8hCSlAaKJ0BBEQFBBQseFiwUVkURHEgtjWdV0bLPpTV10BRYodsWH5i64guIiAqKyASu8lgfTeJzNz/8 ........

解析时只要“base64,”后面的部分,所以需要截取一下。图片数据通过表单传递到了后台,可以在后台获取到信息。但是有一点必须注意:数据中的 "+"加号会因为传递变为 " "空格,所以需要替换一下。

后台代码实现过程如下:

1.创建Excel工作簿

2.创建sheet页

3.创建绘图画布(这里注明,一个sheet只能创建一个画布,但一个画布中可以添加多张图片)

4.处理前台传来的base64编码

5.将BASE64编码格式转为byte数组

6.利用HSSFPatriarch将图片以流的方式写入EXCEL

7.下载Excel

由于我是多个图片导入到一个Excel的一个sheet中,所以把4、5、6步骤封装到一个方法中,避免代码重复

详细代码如下

/**
	 * 科研项目数据分析-----保存为图片导出Excel
	 * @param request
	 * @param response
	 * @throws Exception 
	 */
	@RequestMapping(value = "/export/exportForEchartsImg")
	public void exportForEchartsImg(@RequestParam(value = "picBase64InfoCostStr") String picBase64InfoCostStr,
			@RequestParam(value = "picBase64InfoCategory") String picBase64InfoCategory,
			@RequestParam(value = "picBase64InfoExpectedGain") String picBase64InfoExpectedGain,
			@RequestParam(value = "picBase64InfoProjectType") String picBase64InfoProjectType,
			HttpServletRequest request,HttpServletResponse response) throws Exception {
		/*
		 * 数据样式:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABI4AAAEsCAYAAAClh/jbAAAgAElEQVR4XuzdB3hUVfo
		 *        /8O+dSe8hCSlAaKJ0BBEQFBBQseFiwUVkURHEgtjWdV0bLPpTV10BRYodsWH5i64guIiAqKyASu8lgfTeJzNz/8 ........
		 * 图片编码只要‘base64,’后面的部分
		 * 图片数据通过表单传递到了后台,可以在后台获取到信息。但是有一点必须注意:数据中的 "+"加号会因为传递变为 " "空格,所以需要替换一下
		 */
		String dataChartCostStr = (URLDecoder.decode(picBase64InfoCostStr.substring(22),"UTF-8")).replaceAll(" ", "+");
		String dataChartCategory = (URLDecoder.decode(picBase64InfoCategory.substring(22),"UTF-8")).replaceAll(" ", "+");
		String dataChartExpectedGain = (URLDecoder.decode(picBase64InfoExpectedGain.substring(22),"UTF-8")).replaceAll(" ", "+");
		String dataChartProjectType = (URLDecoder.decode(picBase64InfoProjectType.substring(22),"UTF-8")).replaceAll(" ", "+");
		//创建Excel工作簿
		HSSFWorkbook wb = new HSSFWorkbook();
		//创建sheet页
		HSSFSheet sheet = wb.createSheet("可视数据");
		//创建绘图(画布),注明:一个sheet只能创建一个画布,但一个画布中可以添加多张图片
		HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
		//费用构成-图表图片
		createPictureInExcel(dataChartCostStr, patriarch, wb, (short) 1, 1, (short) 11, 25);
		//所述领域-图表图片
		createPictureInExcel(dataChartCategory, patriarch, wb, (short) 1, 27, (short) 11, 53);
		//预期增益-图表图片
		createPictureInExcel(dataChartExpectedGain, patriarch, wb, (short) 1, 55, (short) 11, 81);
		//项目类别-图表图片
		createPictureInExcel(dataChartProjectType, patriarch, wb, (short) 1, 83, (short) 22, 109);
		//图片写入Excel
		OutputStream out = response.getOutputStream();
		response.setContentType("application/vnd.ms-excel");
		response.setHeader("Content-Disposition", "attachment;filename=" + new String("echarts.xls".getBytes("gb2312"), "ISO8859-1" ));
		wb.write(out);
		wb.close();
	}
	
	/**
	 * 将图片保存到excel中
	 * @param dataChart  图片的BASE64格式编码
	 * @param patriarch  Excel-sheet 画布
	 * @param wb         Excel工作簿
	 * @param col1                   指定起始的单元格行索引
	 * @param row1                   指定起始的单元格列索引
	 * @param col2                   指定结束的单元格行索引
	 * @param row2                   指定结束的单元格列索引
	 * @throws Exception
	 */
	@SuppressWarnings("restriction")
	public void createPictureInExcel(String dataChart,HSSFPatriarch patriarch,HSSFWorkbook wb,
			short col1, int row1,short col2, int row2) throws Exception{
		//用于将BASE64编码格式转为byte数组
		BASE64Decoder base64Decoder = new BASE64Decoder();
		ByteArrayOutputStream dataChartoutStream = new ByteArrayOutputStream();
		//将dataChartStringin作为输入流,读取图片存入image中
		ByteArrayInputStream dataChartin = new ByteArrayInputStream( base64Decoder.decodeBuffer(dataChart));
		BufferedImage dataChartbufferImg = ImageIO.read(dataChartin); 
		//利用HSSFPatriarch将图片写入EXCEL
		ImageIO.write(dataChartbufferImg, "png", dataChartoutStream); 
		/*
		 * 指定绘图区域位置及大小
		 * HSSFClientAnchor(int dx1, int dy1, int dx2, int dy2, short col1, int row1, short col2, int row2)
		 * 参数说明:
		 * dx1 dy1 起始单元格中的x,y坐标.
		 * dx2 dy2 结束单元格中的x,y坐标.
		 * col1,row1 指定起始的单元格,下标从0开始.
		 * col2,row2 指定结束的单元格 ,下标从0开始.
		 * 详情参考博客 https://www.cnblogs.com/1175429393wljblog/p/9809868.html
		 */
		HSSFClientAnchor anchorCostStr = new HSSFClientAnchor(0, 0, 0, 0, col1, row1, col2, row2);
		//画图
		patriarch.createPicture(anchorCostStr, wb.addPicture(dataChartoutStream.toByteArray(), HSSFWorkbook.PICTURE_TYPE_PNG));
	}

这样就可以完成功能了,导出的Excel如下

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值