前言
通过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。