后台生成echarts图片

前言

  通过echarts的jar包,Java后台生成一张图片,并把图片插入到word中。关于word插图片的代码在下一章。

  首先需要工具phantomjs,echarts-convert.js,jquery.js,echarts.js。

  工程是spring-boot的工程,环境是windows,当然,我在Linux上也验证过了,可以生成图片,但是要把phantomjs的权限改为  +x 。

工具准备

  首先下载phantomjs  

      github地址: https://github.com/ariya/phantomjs

  下载工具地址: https://phantomjs.org/download.html

  我是直接下载了windows和64的linux。

  

  echarts-converts.js准备

  这个是我从别人博客里直接拷贝过来的,也不知道下载地址在哪。

(function () {
var system = require('system');
var fs = require('fs');
var config = {
    // define the location of js files
    JQUERY: 'jquery-3.6.3.min.js',
    //ESL: 'esl.js',
    ECHARTS: 'echarts.min.js',
    // default container width and height
    DEFAULT_WIDTH: '1920',
    DEFAULT_HEIGHT: '800'
}, parseParams, render, pick, usage;

usage = function () {
    console.log("\nUsage: phantomjs echarts-convert.js -options options -outfile filename -width width -height height"
        + "OR"
        + "Usage: phantomjs echarts-convert.js -infile URL -outfile filename -width width -height height\n");
};

pick = function () {
    var args = arguments, i, arg, length = args.length;
    for (i = 0; i < length; i += 1) {
        arg = args[i];
        if (arg !== undefined && arg !== null && arg !== 'null' && arg != '0') {
            return arg;
        }
    }
};

parseParams = function () {
    var map = {}, i, key;
    if (system.args.length < 2) {
        usage();
        phantom.exit();
    }
    for (i = 0; i < system.args.length; i += 1) {
        if (system.args[i].charAt(0) === '-') {
            key = system.args[i].substr(1, i.length);
            if (key === 'infile') {
                // get string from file
                // force translate the key from infile to options.
                key = 'options';
                try {
                    map[key] = fs.read(system.args[i + 1]).replace(/^\s+/, '');
                } catch (e) {
                    console.log('Error: cannot find file, ' + system.args[i + 1]);
                    phantom.exit();
                }
            } else {
                map[key] = system.args[i + 1].replace(/^\s+/, '');
            }
        }
    }
    return map;
};

render = function (params) {
    var page = require('webpage').create(), createChart;

    var bodyMale = config.SVG_MALE;
    page.onConsoleMessage = function (msg) {
        console.log(msg);
    };

    page.onAlert = function (msg) {
        console.log(msg);
    };

    createChart = function (inputOption, width, height,config) {
        var counter = 0;
        function decrementImgCounter() {
            counter -= 1;
            if (counter < 1) {
                console.log(messages.imagesLoaded);
            }
        }

        function loadScript(varStr, codeStr) {
            var script = $('<script>').attr('type', 'text/javascript');
            script.html('var ' + varStr + ' = ' + codeStr);
            document.getElementsByTagName("head")[0].appendChild(script[0]);
            if (window[varStr] !== undefined) {
                console.log('Echarts.' + varStr + ' has been parsed');
            }
        }

        function loadImages() {
            var images = $('image'), i, img;
            if (images.length > 0) {
                counter = images.length;
                for (i = 0; i < images.length; i += 1) {
                    img = new Image();
                    img.onload = img.onerror = decrementImgCounter;
                    img.src = images[i].getAttribute('href');
                }
            } else {
                console.log('The images have been loaded');
            }
        }
        // load opitons
        if (inputOption != 'undefined') {
            // parse the options
            loadScript('options', inputOption);
            // disable the animation
            options.animation = false;
        }

        // we render the image, so we need set background to white.
        $(document.body).css('backgroundColor', 'white');
        var container = $("<div>").appendTo(document.body);
        container.attr('id', 'container');
        container.css({
            width: width,
            height: height
        });
        // render the chart
        var myChart = echarts.init(container[0]);
        myChart.setOption(options);
        // load images
        loadImages();
        return myChart.getDataURL();
    };

    // parse the params
    page.open("about:blank", function (status) {
        // inject the dependency js
        page.injectJs(config.ESL);
        page.injectJs(config.JQUERY);
        page.injectJs(config.ECHARTS);


        var width = pick(params.width, config.DEFAULT_WIDTH);
        var height = pick(params.height, config.DEFAULT_HEIGHT);

        // create the chart
        var base64 = page.evaluate(createChart, params.options, width, height,config);
        fs.write("base64.txt",base64);
        // define the clip-rectangle
        page.clipRect = {
            top: 0,
            left: 0,
            width: width,

            height: height
        };
        // render the image
        page.render(params.outfile);
        console.log('render complete:' + params.outfile);
        // exit
        phantom.exit();
    });
};
// get the args
var params = parseParams();

// validate the params
if (params.options === undefined || params.options.length === 0) {
    console.log("ERROR: No options or infile found.");
    usage();
    phantom.exit();
}
// set the default out file
if (params.outfile === undefined) {
    var tmpDir = fs.workingDirectory + '/tmp';
    // exists tmpDir and is it writable?
    if (!fs.exists(tmpDir)) {
        try {
            fs.makeDirectory(tmpDir);
        } catch (e) {
            console.log('ERROR: Cannot make tmp directory');
        }
    }
    params.outfile = tmpDir + "/" + new Date().getTime() + ".png";
}

// render the image
render(params);
}());

  echarts.min.js下载

  去echarts官网下载就可以了,https://echarts.apache.org/

 

  jquery-3.6.3下载

  也是很常见的。

 

工具都准备好了以后,先放在一起,然后知道命令  phantomjs echarts-convert.js -infile URL -outfile filename -width width -height height。

这个命令其实在echarts-converts.js中有,但我是windows运行的,所以需要的是 phantomjs.exe

在其中也有config,所以需要按照你自己下载的配置config,要保持名字是一样的。

var config = {
    // define the location of js files
    JQUERY: 'jquery-3.6.3.min.js',
    //ESL: 'esl.js',
    ECHARTS: 'echarts.min.js',
    // default container width and height
    DEFAULT_WIDTH: '1920',
    DEFAULT_HEIGHT: '800'
}, parseParams, render, pick, usage;

 

工程配置

  新建一个spring boot工程后,把上面的东西全放在resources下,如下图所示

  引入echarts的jar包

  里面要排除日志的包,我这里是和工程冲突,工程无法启动。纯净的spring boot不知道冲突否,这个没试过。

<!--echarts-->
<dependency>
    <groupId>org.icepear.echarts</groupId>
    <artifactId>echarts-java</artifactId>
    <version>1.0.7</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
        </exclusion>
    </exclusions>
</dependency>

 

  编写生成柱图的代码

Bar bar = new Bar();
bar.setLegend()
    .setTitle(barOption.title)
    .setTooltip("item")
    .addXAxis(barOption.xAxis.toArray(new String[barOption.xAxis.size()]))
    .addYAxis();

for(Map<String,Double[]> map : barOption.series){
    Set<String> keySet = map.keySet();
    for (String key : keySet) {
        bar.addSeries(key,map.get(key));
    }
}

Option option = bar.getOption();
option.setAnimation(false);
{
    //图例大小和位置
    Legend legendOption = new Legend();
    LabelOption labelOption = new Label();
    labelOption.setFontSize(40);
    legendOption.setTextStyle(labelOption);

    //            legendOption.setTop("bottom");//设置到底部后由于字体太大,遮挡
    option.setLegend(legendOption);
}

{
    CategoryAxisLabel labelBaseOption = new CategoryAxisLabel();
    labelBaseOption.setFontSize(40);//设置X轴字体大小

    CategoryAxis xAxis = ((CategoryAxis)((AxisOption[])option.getXAxis())[0]);
    xAxis.setAxisLabel(labelBaseOption);
}

{
    ValueAxisLabel valueAxisLabel = new ValueAxisLabel();
    valueAxisLabel.setFontSize(40);//设置Y轴字体

    ValueAxis yAxis = ((ValueAxis)((AxisOption[])option.getYAxis())[0]);
    yAxis.setAxisLabel(valueAxisLabel);
}

Engine engine = new Engine();

  engine就是用来渲染生成柱图,其中可以调用render系列的方法。

  我需要生成图片,但这个本身没有生成图片的方法,所以是用phantomjs模拟浏览器行为,然后将渲染好的柱图通过echarts-convert.js保存为图片。

  执行命令的代码,

String cmd = phantomPath +" "+echartConvertPath + " -infile " + optionPath + " -outfile " + picturePath;
logger.info(cmd);

Process process = null;
if (osName.toLowerCase().startsWith("win")) {
    process = Runtime.getRuntime().exec("cmd /c " + cmd);
} else {
    process = Runtime.getRuntime().exec(cmd);
}
// 打印控制台信息
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = "";
while ((line = input.readLine()) != null) {
    logger.info(line);
}
input.close();

  上面需要文件路径,如下

String optionPath = this.getClass().getClassLoader().getResource("echarts/barOption.js").getPath();
optionPath = URLDecoder.decode(optionPath, "UTF-8");
optionPath = this.handlePath(optionPath,osName);

String phantoPathName = "phantomjs";
if (osName.toLowerCase().startsWith("win")) {
    phantoPathName = "phantomjs.exe";
}
// 变成了这种样子就需要改掉了  /E:/xyhj/project/esg/esg-ats-serve/portal_server/target/classes/echarts/barOption.js
String phantomPath = this.getClass().getClassLoader().getResource("echarts/"+phantoPathName).getPath();
phantomPath = URLDecoder.decode(phantomPath, "UTF-8");
phantomPath = this.handlePath(phantomPath,osName);

String echartConvertPath = this.getClass().getClassLoader().getResource("echarts/echarts-convert.js").getPath();
echartConvertPath = URLDecoder.decode(echartConvertPath, "UTF-8");
echartConvertPath = this.handlePath(echartConvertPath,osName);

String picturePath = this.getClass().getClassLoader().getResource("files").getPath();
picturePath = picturePath+File.separator+barOption.picName;
picturePath = this.handlePath(picturePath,osName);


/**
     * 处理这种前面多了一个斜杠的路径  /E:/xyhj/project/esg/esg-ats-serve/portal_server/target/classes/echarts/barOption.js
     * linux下是  /home/XXX/XXX路径,前面这/ 不能去掉
     * @param path
     * @return
     */
private String handlePath(String path,String osName){
    if (osName.toLowerCase().startsWith("win")) {
        if(path.startsWith("/")){
            path = path.substring(1);
        }
    }
    return path;
}

 

完整代码

package com.staryea.ats.report;

import com.app.frame.util.PageData;
import com.staryea.app.out_interface.OuterInterService;
import com.staryea.logging.Log;
import com.staryea.logging.LogFactory;
import org.icepear.echarts.Bar;
import org.icepear.echarts.Option;
import org.icepear.echarts.components.coord.CategoryAxisLabel;
import org.icepear.echarts.components.coord.ValueAxisLabel;
import org.icepear.echarts.components.coord.cartesian.CategoryAxis;
import org.icepear.echarts.components.coord.cartesian.ValueAxis;
import org.icepear.echarts.origin.coord.AxisLabelBaseOption;
import org.icepear.echarts.origin.coord.ValueAxisLabelOption;
import org.icepear.echarts.origin.coord.cartesian.AxisOption;
import org.icepear.echarts.render.Engine;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 定义生成各种图表图片
 */
@Service(value="echartsService")
public class EchartsService {
    protected Log logger = LogFactory.getLog(EchartsService.class);

    /**
     * 获取图片
     * 要在linux中找到config/echarts下,执行 chmod 777 phantomjs 让有执行权限
     * @throws Exception
     */
    public PageData generateBarImage(BarOption barOption) throws Exception {
        // 执行命令,window系统需要额外配置
        String osName = System.getProperties().getProperty("os.name");

//        String optionPath = "E:\\workspace\\idea_workspace\\FamilySystem\\src\\main\\java\\com\\lw\\echarts\\barOption.js";
//        String phantomPath = "E:\\workspace\\idea_workspace\\FamilySystem\\src\\main\\java\\com\\lw\\echarts\\phantomjs.exe";
//        String echartConvertPath = "E:\\temp\\resources\\echarts-convert.js";

        String optionPath = this.getClass().getClassLoader().getResource("echarts/barOption.js").getPath();
        optionPath = URLDecoder.decode(optionPath, "UTF-8");
        optionPath = this.handlePath(optionPath,osName);

        String phantoPathName = "phantomjs";
        if (osName.toLowerCase().startsWith("win")) {
            phantoPathName = "phantomjs.exe";
        }
        // 变成了这种样子就需要改掉了  /E:/xyhj/project/esg/esg-ats-serve/portal_server/target/classes/echarts/barOption.js
        String phantomPath = this.getClass().getClassLoader().getResource("echarts/"+phantoPathName).getPath();
        phantomPath = URLDecoder.decode(phantomPath, "UTF-8");
        phantomPath = this.handlePath(phantomPath,osName);

        String echartConvertPath = this.getClass().getClassLoader().getResource("echarts/echarts-convert.js").getPath();
        echartConvertPath = URLDecoder.decode(echartConvertPath, "UTF-8");
        echartConvertPath = this.handlePath(echartConvertPath,osName);

        String picturePath = this.getClass().getClassLoader().getResource("files").getPath();
        picturePath = picturePath+File.separator+barOption.picName;
        picturePath = this.handlePath(picturePath,osName);


        Bar bar = new Bar();
        bar.setLegend()
                .setTitle(barOption.title)
                .setTooltip("item")
                .addXAxis(barOption.xAxis.toArray(new String[barOption.xAxis.size()]))
                .addYAxis();
        for(Map<String,Double[]> map : barOption.series){
            Set<String> keySet = map.keySet();
            for (String key : keySet) {
                bar.addSeries(key,map.get(key));
            }
        }

        Option option = bar.getOption();
        option.setAnimation(false);
        {
            CategoryAxis axisOption = new CategoryAxis();
            CategoryAxisLabel labelBaseOption = new CategoryAxisLabel();
            labelBaseOption.setFontSize(25);//设置X轴字体大小
            axisOption.setAxisLabel(labelBaseOption);
            option.setXAxis(axisOption);
        }

        {
            ValueAxis valueAxis = new ValueAxis();
            ValueAxisLabel valueAxisLabel = new ValueAxisLabel();
            valueAxisLabel.setFontSize(20);//设置Y轴字体
            valueAxis.setAxisLabel(valueAxisLabel);
            option.setYAxis(valueAxis);
        }

        Engine engine = new Engine();
//        String html = engine.renderHtml(bar);

        String optionStr = engine.renderJsonOption(bar);
        FileWriter fileWriter = new FileWriter(new File(optionPath));
        fileWriter.write(optionStr);
        fileWriter.close();

        // 拼接命令
        //在echarts-convert.js中有使用命令的方法 usage
//        String cmd = phantomPath +" "+echartConvertPath + " -options " + optionStr + " -outfile " + picturePath;
        String cmd = phantomPath +" "+echartConvertPath + " -infile " + optionPath + " -outfile " + picturePath;
        logger.info(cmd);

        Process process = null;
        if (osName.toLowerCase().startsWith("win")) {
            process = Runtime.getRuntime().exec("cmd /c " + cmd);
        } else {
            process = Runtime.getRuntime().exec(cmd);
        }
        // 打印控制台信息
        BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line = "";
        while ((line = input.readLine()) != null) {
            logger.info(line);
        }
        input.close();

        PageData pageData = new PageData();
        pageData.put("picPath",picturePath);
        pageData.put("code","0");
        return pageData;
    }

    /**
     * 处理这种前面多了一个斜杠的路径  /E:/xyhj/project/esg/esg-ats-serve/portal_server/target/classes/echarts/barOption.js
     * linux下是  /home/XXX/XXX路径,前面这/ 不能去掉
     * @param path
     * @return
     */
    private String handlePath(String path,String osName){
        if (osName.toLowerCase().startsWith("win")) {
            if(path.startsWith("/")){
                path = path.substring(1);
            }
        }
        return path;
    }

    /**
     * 测试用
     * @return
     */
    public BarOption packageBarOption(){
        String[] x = new String[]{"Matcha Latte", "Milk Tea", "Cheese Cocoa", "Walnut Brownie"};
        BarOption barOption = new BarOption();
        for (String s : x) {
            barOption.xAxis.add(s);
        }

        List<Map<String,Double[]>> series = barOption.series;

        {
            Map<String,Double[]> map = new HashMap<>();
            Double[] value = new Double[]{43.3, 83.1, 86.4, 72.4};
            map.put("2015",value);

            series.add(map);
        }

        {
            Map<String,Double[]> map = new HashMap<>();
            Double[] value = new Double[]{43.3, 83.1, 86.4, 72.4};
            map.put("2016",value);

            series.add(map);
        }

        {
            Map<String,Double[]> map = new HashMap<>();
            Double[] value = new Double[]{43.3, 83.1, 86.4, 72.4};
            map.put("2017",value);

            series.add(map);
        }
        return barOption;
    }

    public static void main(String[] args) {
    }

}

   测试就可以写个接口测试了,

@RestController
@RequestMapping("/portal/echartController")
public class EchartsController extends SpringbootController {

    @Resource(name="echartsService")
    EchartsService echartsService;

    @GetMapping("/barImage")
    @OperLog(name = "生成图片测试", desc = "生成图片测试", action = OperLogEnum.query)
    public PageData barImage() {
        try {
            echartsService.generateBarImage(echartsService.packageBarOption());
        } catch (Exception e) {
            logger.error("生成图片测试异常", e);
            return RespUtil.getErrorResp(RespUtil.SYSTEM_ERROR_CODE,RespUtil.SYSTEM_ERROR_MSG);
        }
        return RespUtil.getSuccessResp();
    }
}

  

  然后就可以在项目的编译目录下有图片了。

  在项目里生成图片插入后是下面这样,下一篇介绍如何把图片插入word。

 

posted @ 2023-03-01 13:51  伟衙内  阅读(1542)  评论(0编辑  收藏  举报