Bootstrap

echarts通过poi导出图片到excel文件

最近在做统计报表,对比了一下echarts、flot、Highcharts,还是选择了开源的echarts,主要表现美观,而且对IE8支持好(😭😭😭兼容IE8什么的最讨厌了),整理了一下支持导出多个echarts图表到excel文档的方法,而且导出到excel中是按图片正常比例显示的,提交表单防跳转。
导出成品图
在这里插入图片描述

1.首先看echarts代码,以柱状图和饼状图为例
html代码

<!-- 柱状图 -->
<div class="echarts" id="useStats-bar-chart"></div>
<!-- 饼状图 -->
<div class="echarts" id="useStats-pie-chart"></div>

javascript代码

// 柱状图
var barChart = echarts.init(document.getElementById("useStats-bar-chart"));
var baroption = {
	// 背景要设置成白色,不然导出来乌漆嘛黑的看不清
    backgroundColor: '#ffffff',
    tooltip : {
        trigger: 'axis',
        axisPointer: {            // 坐标轴指示器,坐标轴触发有效
            type: 'shadow'        // 默认为直线,可选为:'line' | 'shadow'
        }
    },
    grid:{
        x:30,
        y:48,
        x2:40,
        y2:66
    },
    calculable : true,
    xAxis : [
        {
            type : 'category',
            data : ['乌鲁木齐市','克拉玛依市','吐鲁番地区',
                '哈密地区','昌吉回族自治州','博尔塔拉蒙古自治州',
                '巴音郭楞蒙古自治州','阿克苏地区','克孜勒苏柯尔克孜自治州',
                '喀什地区','和田地区','伊犁哈萨克自治州',
                '塔城地区','阿勒泰地区','石河子',
                '阿拉尔','图木舒克','五家渠'],
            axisLabel: {
                interval: 0,// 间隔显示数,0表示全部显示
                //坐标轴刻度标签的相关设置。
                formatter : function(params){
                    // 换行
                    return chartsLinefeed(params);
                }
            }
        }
    ],
    yAxis : [
        {
            type : 'value'
        }
    ],
    series : [
        {
            name:'xxx使用量',
            type:'bar',
            data:[300, 50, 100, 30, 50, 60, 20, 80, 70, 50, 30, 20, 70, 300, 20, 150, 45, 90],
            markPoint : {
                data : [
                    {type : 'max', name: '最大值'},
                    {type : 'min', name: '最小值'}
                ]
            },
            markLine : {
                data : [
                    {type : 'average', name: '平均值'}
                ]
            },
            itemStyle: {
                normal: {
                    color: 'rgba(28,132,198,.6)',//柱子颜色
                    // borderColor: '#1c84c6',//边框颜色
                    barBorderWidth: 3,
                    barBorderRadius: 2,
                    label: {
                        show: true,
                        position: 'insideLeft'
                    }
                }
            }
        }
    ]
};
barChart.setOption(baroption);
window.onresize = barChart.resize;
// 饼状图
var pieChart = echarts.init(document.getElementById("useStats-pie-chart"));
var pieoption = {
    backgroundColor: '#ffffff',
    tooltip : {
        trigger: 'item',
        formatter: "{a} <br/>{b} : {c} ({d}%)"
    },
    legend: {
        orient : 'vertical',
        x : 'left',
        data:['乌鲁木齐市','克拉玛依市','吐鲁番地区',
            '哈密地区','昌吉回族自治州','博尔塔拉蒙古自治州',
            '巴音郭楞蒙古自治州','阿克苏地区','克孜勒苏柯尔克孜自治州',
            '喀什地区','和田地区','伊犁哈萨克自治州',
            '塔城地区','阿勒泰地区','石河子',
            '阿拉尔','图木舒克','五家渠']
    },
    calculable : true,
    series : [
        {
            name:'xxx使用量',
            type:'pie',
            radius : '80%',
            center: ['50%', '50%'],
            /*在series中添加itemStyle即可直观显示饼型数值*/
            itemStyle:{
                normal:{
                    label:{
                        show: true,
                        formatter: '{b} : {c} ({d}%)'
                    },
                    labelLine :{show:true}
                }
            },
            data:[
                {value:300, name:'乌鲁木齐市'},
                {value:50, name:'克拉玛依市'},
                {value:100, name:'吐鲁番地区'},
                {value:30, name:'哈密地区'},
                {value:50, name:'昌吉回族自治州'},
                {value:60, name:'博尔塔拉蒙古自治州'},
                {value:20, name:'巴音郭楞蒙古自治州'},
                {value:80, name:'阿克苏地区'},
                {value:70, name:'克孜勒苏柯尔克孜自治州'},
                {value:50, name:'喀什地区'},
                {value:30, name:'和田地区'},
                {value:20, name:'伊犁哈萨克自治州'},
                {value:70, name:'塔城地区'},
                {value:300, name:'阿勒泰地区'},
                {value:20, name:'石河子'},
                {value:150, name:'阿拉尔'},
                {value:45, name:'图木舒克'},
                {value:90, name:'五家渠'}
            ]
        }
    ]
};
pieChart.setOption(pieoption);
$(window).resize(pieChart.resize);

上成品图:
在这里插入图片描述
在这里插入图片描述
2.导出功能
html代码:

<!--form的target指定一个空的iframe,防止导出后跳转页面-->
<iframe name="none_iframe" style="display:none;"></iframe>
<form method="post" id="none_form" target="none_iframe" style="display:none"></form>

javascript代码

// 导出图表
function exportCharts(){
	// excel的文件名称
    var fileName = "xxx使用统计图表";
    /**
    * 柱状图的内容,base64格式数据,这里支持多个图表导出,有几个图就写几个fileContent和fileSize,只有一个的话写一个就够了
    * 图片格式data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABI4AAAEsCAYAAAClh/jbAAAgAElEQVR4XuzdB3hUVfo/8O+dSe8hCSlAaKJ0BBEQFBBQseFiwUVkURHEgtjWdV0bLPpTV10BRYodsWH5i64guIiAqKyASu8lgfTeJzNz/8 ........
    **/
    var fileContent1 = encodeURIComponent(barChart.getDataURL());
    // 饼状图内容,需要encodeURIComponent编码,防止传输过程符号丢失
    var fileContent2 = encodeURIComponent(pieChart.getDataURL());
    // 图片宽度和高度,为了计算图片比例,以便在excel中以正常比例显示,不至于拉伸
    var fileSize1 = barChart.getWidth() + ":" + barChart.getHeight();
    var fileSize2 = pieChart.getWidth() + ":" + pieChart.getHeight();
    // form表单的请求地址,ctx是你的项目地址
    $("#none_form").attr("action", ctx + "cozuxg/eeiduc");
    $("#none_form").append("<input type=hidden name='fileName' value='"+fileName+"'/>");
    $("#none_form").append("<input type=hidden name='fileContents' value='"+fileContent1+"'/>");
    $("#none_form").append("<input type=hidden name='fileContents' value='"+fileContent2+"'/>");
    $("#none_form").append("<input type=hidden name='fileSizes' value='"+fileSize1+"'/>");
    $("#none_form").append("<input type=hidden name='fileSizes' value='"+fileSize2+"'/>");
    // 提交表单
    $("#none_form").submit();
    // 删除本次表单数据,这里要删除子元素,不然下次导出不了
    $("#none_form").empty();
}

java代码,这部分是公用的,所有导出echarts图表都调这一个接口


    /**
     * 通用导出图片到excel
     * @param fileContents base64图片内容
     * @param fileName excel文件名
     * @param fileSizes 文件尺寸,宽:高,因为在excel中是按单元格来设置图片的宽和高,所以要按图片的宽高比例算出在excel中需要多少个单元格
     * @param request 请求
     * @param response 返回
     */
    @PostMapping("/eeiduc")
    public void exportExcelForImg(String[] fileContents, String fileName, String[] fileSizes,
                             HttpServletRequest request,HttpServletResponse response) throws Exception
    {
        //创建Excel工作簿,xls格式用HSSFWorkbook,xlsx格式用SXSSFWorkbook
        HSSFWorkbook wb = new HSSFWorkbook();
        //创建sheet页
        HSSFSheet sheet = wb.createSheet(fileName);
        //创建绘图(画布),注明:一个sheet只能创建一个画布,但一个画布中可以添加多张图片
        HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
        
        String fileContent;
        String fileSize;
        // 图片宽度
        Double width;
        // 图片高度
        Double height;
        // 指定起始的单元格行索引,默认1
        int col1 = 1;
        // 指定起始的单元格列索引,row1=上一个row2+1,+1是为了两个图片之间隔开一行
        int row1;
        // 指定结束的单元格行索引,相当于宽,默认18
        int col2 = 18;
        // 指定结束的单元格列索引,相当于高,row2=row1+col2*(height/width)*3.5,乘以3.5是因为单元格的宽是高的3.5倍
        int row2 = 0;
        // 支持多个图表导入
        for(int i = 0; i < fileContents.length;i++){
            fileContent = fileContents[i];
            if(fileContent.length() > 0){
                fileSize = fileSizes[i];
                width = Double.valueOf(fileSize.split(":")[0]);
                height = Double.valueOf(fileSize.split(":")[1]);
                // 图片编码只要‘base64,’后面的部分
                fileContent = URLDecoder.decode(fileContent,"UTF-8").substring(22);
                row1 = row2 + 1;
                row2 = row1 + (int) Math.ceil(col2 * (height / width) * 3.5);
                ExcelUtil.createPictureInExcel(fileContent, patriarch, wb, (short) col1, row1, (short) col2, row2);
            }
        }
        // 重命名,文件名前面加时间,如20200805135638_xxx使用统计.xls,这部分代码按项目风格自行来定
        String timeStr = DateUtils.parseDateToStr("yyyyMMddHHmmss", DateUtils.getNowDate());
        fileName = timeStr + "_" + fileName + ".xls";
        //图片写入Excel
        OutputStream out = response.getOutputStream();
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("gb2312"), "ISO8859-1" ));
        wb.write(out);
        wb.close();
    }

ExcelUtil.createPictureInExcel代码


    /**
     * 将图片保存到excel中
     * @param dataChart  图片的BASE64格式编码
     * @param patriarch  Excel-sheet 画布
     * @param wb         Excel工作簿
     * @param col1  指定起始的单元格行索引
     * @param row1  指定起始的单元格列索引
     * @param col2  指定结束的单元格行索引
     * @param row2  指定结束的单元格列索引
     * @throws Exception
     */
    @SuppressWarnings("restriction")
    public static 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开始.
         */
        HSSFClientAnchor anchorCostStr = new HSSFClientAnchor(0, 0, 0, 0, col1, row1, col2, row2);
        //画图
        patriarch.createPicture(anchorCostStr, wb.addPicture(dataChartoutStream.toByteArray(), HSSFWorkbook.PICTURE_TYPE_PNG));
    }

导出成品图
在这里插入图片描述

我这里是用固定宽度18个单元格来算高度需要多少个单元格,如果有需要也可以用固定高度来算宽度,到这里就大功告成了,祝大家工作愉快!

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;