继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

SpringBoot纯后台生成Echarts图片(三)

霜花似雪
关注TA
已关注
手记 163
粉丝 1.5万
获赞 8507

http://img4.mukewang.com/5ea969f50001efe106780454.jpg

SpringBoot纯后台生成Echarts图片(一)

SpringBoot纯后台生成Echarts图片(二)

一、项目工程结构

http://img4.sycdn.imooc.com/5da6d0ff0001855d04210683.jpg

二.项目依赖说明

项目的pom.xml配置,属性配置 见第一篇

三.项目代码说明

(1)echarts-pojo模块(数据模型)

package com.lhf.springboot.echarts.pojo;

import lombok.Data;

/**
 * @ClassName: PieData
 * @Author: liuhefei
 * @Description: TODD
 */
@Data
public class PieData {

    private String[] types;
    private String[] datas;
    private String title;

}

(2)controler模块(API接口)

package com.lhf.springboot.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lhf.springboot.common.JsonResult;
import com.lhf.springboot.echarts.pojo.BarData;
import com.lhf.springboot.echarts.pojo.LinesData;
import com.lhf.springboot.echarts.pojo.PieData;
import com.lhf.springboot.util.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.time.FastDateFormat;
import org.jboss.logging.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.awt.*;
import java.io.File;
import java.io.FileOutputStream;
import java.text.DecimalFormat;
import java.util.*;
import java.util.List;

/**
 * @ClassName: EchartsTemplateController
 * @Author: liuhefei
 * @Description: 使用模板生成饼图
 */
@Api(value = "Freemarker模板生成Echarts图表Api接口", tags = "模板生成图表Api-1")
@RequestMapping("/template")
@RestController
public class EchartsTemplateController {

    private final static Logger logger = Logger.getLogger(EchartsController.class);

    @Value("${img-url}")
    private String imgUrl;

    @Value("${img-url-path}")
    private String imgUrlPath;

    @Value("${request-url}")
    private String requestUrl;

    private static final String path = FreemarkerUtil.class.getClassLoader().getResource("").getPath();
    private String temPath = path + "templates";

    
    @ApiOperation("模板生成饼图, html文件格式")
    @RequestMapping(value = "/pie", method = RequestMethod.POST)
    public JsonResult createPieFtl(@RequestBody PieData pieData) {
        String title = pieData.getTitle();
        String[] types = pieData.getTypes();
        String[] datas = pieData.getDatas();

        if (title == null || types == null || datas == null) {
            return new JsonResult(-1, "参数异常");
        }

        if(types.length != datas.length){
            return new JsonResult(-1, "数据不对应");
        }


        //组装参数
        Double data = null;
        Double sum = 0.0;

        DecimalFormat df = new DecimalFormat(".00");

        List<String> stringList = new ArrayList<>();
        String bfb = null;
        String typeParam = null;
        List<Map<String, Object>> listMap = new ArrayList<>();

        for(int i = 0;i<types.length;i++){
            data = Double.valueOf(datas[i]);
            System.out.println("data = " + data);
            sum += Double.valueOf(data);
        }
        System.out.println("sum = " + sum);

        for(int i =0 ;i<types.length;i++){
            data = Double.valueOf(datas[i]);
            bfb = String.valueOf(df.format(data/sum * 100));
            System.out.println("bfb = " + bfb);
            typeParam = types[i] + ":" + datas[i] + "(" + bfb + "%)";
            System.out.println(typeParam);
            //1.组装types
            stringList.add(typeParam);

            //2.组装datas
            Map<String, Object> mapStr = new HashMap<>();
            mapStr.put("name", typeParam);
            mapStr.put("value", datas[i]);

            listMap.add(mapStr);

        }

        System.out.println(stringList);
        System.out.println(listMap);

        try {
            //组装参数
            HashMap<String, Object> pieDatas = new HashMap<>();
            pieDatas.put("title", title);
            //pieDatas.put("types", JSON.toJSONString(types));
            pieDatas.put("types", JSON.toJSONString(stringList));
            pieDatas.put("datas", JSON.toJSONString(listMap));
            String option = FreemarkerUtil.generateString("pieOption1.ftl", temPath, pieDatas);
            System.out.println("option = " + option);

            Map<String, Object> pieOption = new HashMap<>();
            pieOption.put("option", option);
            String htmlPie = FreemarkerUtil.generateString("pieOption.ftl", temPath, pieOption);
            System.out.println("htmlPie = " + htmlPie);

            //写入html
            String nowStr = FastDateFormat.getInstance("yyyyMMddHHmmss").format(new Date());
            String imageName = "pie"+nowStr;
            File htmlFile = new File(imgUrl+imageName+".html");
            if(!htmlFile.exists()){
                htmlFile.createNewFile();
            }
            byte bytes[] = new byte[1024];
            bytes = htmlPie.getBytes();
            int b = bytes.length;  //字节长度
            FileOutputStream fos = new FileOutputStream(htmlFile);
            fos.write(bytes, 0, b);
            fos.write(bytes);
            fos.close();

        } catch (Exception e) {
            logger.error("发生了异常," + e.getMessage());
            return new JsonResult(-1, "Fail");
        }
        return new JsonResult(1, "SUCCESS");
    }

    @ApiOperation("模板生成饼图")
    @RequestMapping(value = "/pieImg", method = RequestMethod.POST)
    public JsonResult createPieFtlImage(@RequestBody PieData pieData) {
        String title = pieData.getTitle();
        String[] types = pieData.getTypes();
        String[] datas = pieData.getDatas();

        if (title == null || types == null || datas == null) {
            return new JsonResult(-1, "参数异常");
        }

        if(types.length != datas.length){
            return new JsonResult(-1, "数据不对应");
        }


        //组装参数
        Double data = null;
        Double sum = 0.0;

        DecimalFormat df = new DecimalFormat(".00");

        List<String> stringList = new ArrayList<>();
        String bfb = null;
        String typeParam = null;
        List<Map<String, Object>> listMap = new ArrayList<>();

        for(int i = 0;i<types.length;i++){
            data = Double.valueOf(datas[i]);
            System.out.println("data = " + data);
            sum += Double.valueOf(data);
        }
        System.out.println("sum = " + sum);

        for(int i =0 ;i<types.length;i++){
            data = Double.valueOf(datas[i]);
            bfb = String.valueOf(df.format(data/sum * 100));
            System.out.println("bfb = " + bfb);
            typeParam = types[i] + ":" + datas[i] + "(" + bfb + "%)";
            System.out.println(typeParam);
            //1.组装types
            stringList.add(typeParam);

            //2.组装datas
            Map<String, Object> mapStr = new HashMap<>();
            mapStr.put("name", typeParam);
            mapStr.put("value", datas[i]);

            listMap.add(mapStr);

        }

        System.out.println(stringList);
        System.out.println(listMap);

        String oldFilePath = null;
        String newFilePath = null;

        try {
            //组装参数
            HashMap<String, Object> pieDatas = new HashMap<>();
            pieDatas.put("title", title);
            //pieDatas.put("types", JSON.toJSONString(types));
            pieDatas.put("types", JSON.toJSONString(stringList));
            pieDatas.put("datas", JSON.toJSONString(listMap));
            String option = FreemarkerUtil.generateString("pieOption1.ftl", temPath, pieDatas);
            System.out.println("option = " + option);

            long nowStr = Calendar.getInstance().getTimeInMillis();
            String imageName = "pie"+nowStr+ RandomUtils.getRandomString(4)+".png";

            Map<String, Object> opt = (Map<String, Object>) JSONObject.parse(option);
            PhantomJS js = new PhantomJS();
            js.setOpt(opt);
            js.setReqMethod("echarts");
            js.setFile(imgUrlPath+imageName);
            PhantomJSUtil.phantomJS(requestUrl, JSON.parseObject(JSON.toJSONString(js)));

            oldFilePath = imgUrlPath+imageName;
            newFilePath = imgUrlPath+"new"+imageName;

            logger.info("oldFilePath = " + oldFilePath);
            logger.info("newFilePath = " + newFilePath);

            String logoText = "霜花似雪";

            //添加水印
            ImageUtil.markImageByText(logoText, oldFilePath, newFilePath, -15, new Color(190,190,190), "png");

        } catch (Exception e) {
            logger.error("发生了异常," + e.getMessage());
            return new JsonResult(-1, "Fail");
        }
        return new JsonResult(1, "SUCCESS");
    }

}

三、插件环境配置说明

插件环境说明见第一篇文章

其他重要说明:

生成饼图的命令:

D:\>cd D:\softpack\echarts\phantomjs-2.1.1-windows\bin

D:\softpack\echarts\phantomjs-2.1.1-windows\bin>phantomjs D:\softpack\echarts\phantomjs-2.1.1-windows\bin\echarts-util.js -s -p 6668
                             或者phantomjs D:\softpack\echarts\phantomjs-2.1.1-windows\bin\echarts-util-one.js -s -p 6668

http://img3.mukewang.com/5ea964c50001a03d09690104.jpg

关键文件说明:

echarts-util.js: 此文件用于生成状图、折线图、饼图, 只限于普通的option(option中没有js方法的)               

echarts-util-one.js: 此文件用于生成状图、折线图、饼图,生成的柱状图具有颜色渐变的效果,option中含有js方法,实例可以参考bar.txt文件    

echarts-util.js代码:

phantom.outputEncoding = "gbk";// 为防止输出中文时出现乱码,可设置输出编码格式,写在最顶部
var params = require('system');// 获取系统参数
    var server = require('webserver').create(); // 服务端
    var port = params.args[3];// 端口,与启动命令有关,不一定是3
    var listen = server.listen(port, function(request, response) {// 监听端口
    var args = serverGetArgs(request);// 得到网络请求参数
    args.response = response;
    methodDis(args);
});
var jslib = {
    jquery : phantom.libraryPath + '/lib/jquery-3.2.1.min.js',
    echarts : phantom.libraryPath + '/lib/echarts.min.js',
    china : phantom.libraryPath + '/lib/china.js',
};
/**
 * 请求分发
 * 
 * @author liansh
 * @data 2019年9月19日 下午11:32:59
 * @param args
 */
function methodDis(args) {
    if (args.reqMethod == "table") {
    table(args);
    } else if (args.reqMethod == "echarts") {
    echarts(args);
    }
    if (args.exit == "true") {
    writeResponse(args.response, {
    error_no : 0
    });
    phantom.exit();
    }
}
function table(args) {
    var page = require('webpage').create();// 打开页面
    // 设置分辨率
    page.viewportSize = {
    width : 1000,
    height : 1200
    };
    // 打开页面
    page.open(args.url || 'http://127.0.0.1:8080/hello', function(status) {
    if (status == "fail") {
    writeResponse(args.response, {
    error_no : -1
    });
    return;
    }
    page.injectJs(jslib.jquery);
    var tableheight = page.evaluate(function() {
    return $('body').height() + 20;
    });
    // 定义剪切范围
    page.clipRect = {
    top : 0,
    left : 0,
    width : 1000,
    height : tableheight
    };
    // var base64 = 'data:image/png;base64,' + page.renderBase64('png');
    page.render(args.file);// 将整个page保存为文件,可以是png,jpg, gif,pdf
    page.close();
    writeResponse(args.response, {
    error_no : 0
    });
    });
    page.onError = function(msg, trace) {
    writeResponse(args.response, {
    error_no : -1,
    error_info : trace
    });
    };
}
function echarts(args) {
    var page = require('webpage').create(); // 客户端
    page.open("about:blank", function(status) {// 空白页
    page.injectJs(jslib.jquery);
    page.injectJs(jslib.echarts);
    page.injectJs(jslib.china);
    var pageBody = page.evaluate(function(args) {
    // 动态加载js,获取options数据
    $('<script>').attr('type', 'text/javascript').html('var options = ' //
    + JSON.stringify(args.opt)).appendTo(document.head);
    // 取消动画,否则生成图片过快,会出现无数据
    if (options !== undefined) {
    options.animation = false;
    }
    // body背景设置为白色
    $(document.body).css('backgroundColor', 'white');
    // echarts容器
    var container = $("<div>").attr('id', 'container').css({
    width : args.width,
    height : args.height
    }).appendTo(document.body);
    var eChart = echarts.init(container[0]);
    eChart.setOption(options);
    }, args);
    // 定义剪切范围
    page.clipRect = {
    top : 0,
    left : 0,
    width : args.width - 100,
    height : args.height + 10
    };
    // var base64 = 'data:image/png;base64,' + page.renderBase64('png');
    // writeResponse(args.response, {// 返回给http请求
    // error_no : 0,
    // base64 : base64
    // });
    page.render(args.file);// 将整个page保存为文件,可以是png,jpg, gif,pdf
    page.close();
    writeResponse(args.response, {
    error_no : 0
    });
    });
    page.onError = function(msg, trace) {
    writeResponse(args.response, {
    error_no : -1,
    error_info : trace
    });
    };
}
function writeResponse(response, msg) {
    response.write(JSON.stringify(msg || {
    error_no : 0
    }));
    response.close();
}
/**
 * 获取请求参数
 * 
 * @author liansh
 * @data 2019年9月19日 下午11:27:16
 * @param request
 * @returns
 */
function serverGetArgs(request) {
    var args = {};
    if ('GET' === request.method) {
    var index = request.url.indexOf('?');
    if (index !== -1) {
    pairs = request.url.substr(index + 1).split("&");
    for (var i = 0; i < pairs.length; i++) {
    var pos = pairs[i].indexOf('=');
    if (pos === -1)
    continue;
    var key = pairs[i].substring(0, pos);
    var value = pairs[i].substring(pos + 1);
    // 中文解码,必须写两层
    value = decodeURIComponent(decodeURIComponent(value));
    args[key] = value;
    }
    }
    } else if ('POST' === request.method) {
    args = JSON.parse(request.post);
    }
    args.width = args.width || 1000;
    args.height = args.height || 400;
    return args;
};

echarts-util-one.js代码:

phantom.outputEncoding = "gbk";// 为防止输出中文时出现乱码,可设置输出编码格式,写在最顶部
var params = require('system');// 获取系统参数
var server = require('webserver').create(); // 服务端
var port = params.args[3];// 端口,与启动命令有关,不一定是3
var listen = server.listen(port, function(request, response) {// 监听端口
    var args = serverGetArgs(request);// 得到网络请求参数
    args.response = response;
    methodDis(args);
});
var jslib = {
    jquery : phantom.libraryPath + '/lib/jquery-3.2.1.min.js',
    echarts : phantom.libraryPath + '/lib/echarts.min.js',
    china : phantom.libraryPath + '/lib/china.js',
};
/**
 * 请求分发
 *
 * @author liansh
 * @data 2019年9月19日 下午11:32:59
 * @param args
 */
function methodDis(args) {
    if (args.reqMethod == "table") {
        table(args);
    } else if (args.reqMethod == "echarts") {
        echarts(args);
    }
    if (args.exit == "true") {
        writeResponse(args.response, {
            error_no : 0
        });
        phantom.exit();
    }
}
function table(args) {
    var page = require('webpage').create();// 打开页面
    // 设置分辨率
    page.viewportSize = {
        width : 1000,
        height : 1200
    };
    // 打开页面
    page.open(args.url || 'http://127.0.0.1:8080/hello', function(status) {
        if (status == "fail") {
            writeResponse(args.response, {
                error_no : -1
            });
            return;
        }
        page.injectJs(jslib.jquery);
        var tableheight = page.evaluate(function() {
            return $('body').height() + 20;
        });
        // 定义剪切范围
        page.clipRect = {
            top : 0,
            left : 0,
            width : 1000,
            height : tableheight
        };
        // var base64 = 'data:image/png;base64,' + page.renderBase64('png');
        page.render(args.file);// 将整个page保存为文件,可以是png,jpg, gif,pdf
        page.close();
        writeResponse(args.response, {
            error_no : 0
        });
    });
    page.onError = function(msg, trace) {
        writeResponse(args.response, {
            error_no : -1,
            error_info : trace
        });
    };
}
function echarts(args) {
    var page = require('webpage').create(); // 客户端
    page.open("about:blank", function(status) {// 空白页
        /**
         * 报错{"file":"undefined","line":3,"function":""},{"file":"","line":18,"function":""}
         * "file":"undefined" 为所引用的jslib文件路径不对,需要重新检查路径
         */
        page.injectJs(jslib.jquery);
        page.injectJs(jslib.echarts);
        page.injectJs(jslib.china);
        var pageBody = page.evaluate(function(args) {
            // 动态加载js,获取options数据
            //var itemStyle = '{"normal":{"color" : new echarts.graphic.LinearGradient(0, 0, 0, 1, [ '+ '{offset : 0,color : "#83bff6"}, {offset : 0.5,color : "#188df0"}, {offset : 1,color : "#188df0"} ])}}';
var itemStyle = '{"normal":{"color" : new echarts.graphic.LinearGradient(0, 0, 0, 1, [ '+ '{offset : 0,color : "#3370FE"}, {offset : 0.5,color : "#3370FE"}, {offset : 1,color : "#3370FE"} ])}}';
            // 动态加载js,获取options数据
            $('<script>').attr('type', 'text/javascript').html('var options =' //
                + JSON.stringify(args.opt).replace('\"__itemStyle"', itemStyle)).appendTo(document.head);
            // 取消动画,否则生成图片过快,会出现无数据
            if (options !== undefined) {
                options.animation = false;
            }
            // body背景设置为白色
            $(document.body).css('backgroundColor', 'white');
            // echarts容器
            var container = $("<div>").attr('id', 'container').css({
                width : args.width,
                height : args.height
            }).appendTo(document.body);
            var eChart = echarts.init(container[0]);
            eChart.setOption(options);
        }, args);
        // 定义剪切范围
        page.clipRect = {
            top : 0,
            left : 0,
            width : args.width - 100,
            height : args.height + 10
        };
        // var base64 = 'data:image/png;base64,' + page.renderBase64('png');
        // writeResponse(args.response, {// 返回给http请求
        // error_no : 0,
        // base64 : base64
        // });
        page.render(args.file);// 将整个page保存为文件,可以是png,jpg, gif,pdf
        page.close();
        writeResponse(args.response, {
            error_no : 0
        });
    });
    page.onError = function(msg, trace) {
        writeResponse(args.response, {
            error_no : -1,
            error_info : trace
        });
    };
}
function writeResponse(response, msg) {
    response.write(JSON.stringify(msg || {
        error_no : 0
    }));
    response.close();
}
/**
 * 获取请求参数
 *
 * @author liansh
 * @data 2019年9月19日 下午11:27:16
 * @param request
 * @returns
 */
function serverGetArgs(request) {
    var args = {};
    if ('GET' === request.method) {
        var index = request.url.indexOf('?');
        if (index !== -1) {
            pairs = request.url.substr(index + 1).split("&");
            for (var i = 0; i < pairs.length; i++) {
                var pos = pairs[i].indexOf('=');
                if (pos === -1)
                    continue;
                var key = pairs[i].substring(0, pos);
                var value = pairs[i].substring(pos + 1);
                // 中文解码,必须写两层
                value = decodeURIComponent(decodeURIComponent(value));
                args[key] = value;
            }
        }
    } else if ('POST' === request.method) {
        args = JSON.parse(request.post);
    }
    args.width = args.width || 1000;
    args.height = args.height || 400;
    return args;
};

四、结合Swagger文档测试

环境配置完成之后,启动服务,swagger文档:http://localhost:8095/swagger-ui.html

测试数据:

{
  "datas": [
    43364, 13899, 12000, 2181, 21798, 1796, 1300
  ],
  "title": "胸罩图例",
  "types": [
    "A罩杯", "B罩杯", "C罩杯", "D罩杯", "E罩杯", "F罩杯","G罩杯"
  ]
}

效果展示:

(1)生成饼图,html文件格式

<!DOCTYPE html>
<html style="height: 100%">
<head>
    <meta charset="utf-8">
</head>
<body style="height: 100%; margin: 0">
<div id="container" style="height: 100%"></div>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts/echarts.min.js"></script>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts-gl/echarts-gl.min.js"></script>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts-stat/ecStat.min.js"></script>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts/extension/dataTool.min.js"></script>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/simplex.js"></script>
<script type="text/javascript">
    var dom = document.getElementById("container");
    var myChart = echarts.init(dom);
    var app = {};
    option = null;
    option ={
    "calculable": true,
    "legend": {
        "data": ["A罩杯:43364(45.01%)","B罩杯:13899(14.43%)","C罩杯:12000(12.46%)","D罩杯:2181(2.26%)","E罩杯:21798(22.63%)","F罩杯:1796(1.86%)","G罩杯:1300(1.35%)"],
        "orient": "vertical",
        "x": "left",
        "textStyle": {
            "color": "red",
            "fontSize": 15,
            "fontWeight": "bolder"
        }
    },
    "series": [{
        "center": ["50%", "60%"],
        "data": [{"name":"A罩杯:43364(45.01%)","value":"43364"},{"name":"B罩杯:13899(14.43%)","value":"13899"},{"name":"C罩杯:12000(12.46%)","value":"12000"},{"name":"D罩杯:2181(2.26%)","value":"2181"},{"name":"E罩杯:21798(22.63%)","value":"21798"},{"name":"F罩杯:1796(1.86%)","value":"1796"},{"name":"G罩杯:1300(1.35%)","value":"1300"}],
        "name": "胸罩图例",
        "radius": "65%",
        "type": "pie",
        "avoidLabelOverlap": true,
        "label": {
            "normal": {
                "show": true,
                "position": "top",
                "textStyle": {
                    "color":"red",
                    "fontSize": "15",
                    "fontWeight": "bold"
                }
            },
            "emphasis": {
                "show": true,
                "textStyle": {
                    "fontSize": "20",
                    "fontWeight": "bold"
                }
            }
        },
        "labelLine": {
            "normal": {
                "show": true
            }
        }
    }],
    "title": {
        "subtext": "",
        "text": "胸罩图例",
        "x": "center",
        "textStyle": {
            "color": "green",
            "fontSize": 20,
            "fontWeight": "bolder"
        }
    },
    "toolbox": {
        "feature": {
            "mark": {
                "lineStyle": {
                    "color": "#1e90ff",
                    "type": "dashed",
                    "width": 2
                },
                "show": true
            },
            "dataView": {
                "lang": ["数据视图", "关闭", "刷新"],
                "readOnly": false,
                "show": true,
                "title": "数据视图"
            },
            "magicType": {
                "show": true,
                "title": {
                    "bar": "柱形图切换",
                    "stack": "堆积",
                    "tiled": "平铺",
                    "line": "折线图切换"
                },
                "type": ["pie", "funnel"]
            },
            "restore": {
                "show": true,
                "title": "还原"
            },
            "saveAsImage": {
                "lang": ["点击保存"],
                "show": true,
                "title": "保存为图片",
                "type": "png"
            }
        },
        "show": true
    },
    "tooltip": {
        "formatter": "{a} <br/>{b} : {c} ({d}%)",
        "trigger": "item"
    }
}
;
    if (option && typeof option === "object") {
        myChart.setOption(option, true);
    }
</script>
</body>
</html>

(2)生成饼图,图片格式,效果图如下

不带水印:

http://img1.mukewang.com/5ea968f00001e2dc08930504.jpg

带水印:

http://img2.mukewang.com/5ea969180001cfc408840482.jpg

更多关于Echarts图表的使用请读者自行研究,如果遇到问题,欢迎与我交流。需要代码的话,也可以找我!

喜欢的话,请点个赞,感谢你的支持!


打开App,阅读手记
2人推荐
发表评论
随时随地看视频慕课网APP

热门评论

你好,phantomjs是否还有linux版本呢

echarts-util.js怎么跑不了,echarts-convert.js可以,也建了lib文件夹放入所需js

大佬能分享一下代码地址吗

查看全部评论