纯后台生成echarts图片-phantomjs-2.1.1

9 篇文章 0 订阅

问题场景

后端需要定时发邮件,邮件正文带图片,图片要求每次即时生成。

开发环境

idea+Java8+springboot2
echart-convert.js
phantomjs-2.1.1与字体

分析

phantomjs可以模拟浏览器执行js请求ajax等效果,俗称无头浏览器,可以用于客户端渲染。

步骤

  1. 下载上述资源,放入工程里,待调用。
  2. 安装phantomjs-2.1.1及微软雅黑字体[图表中文乱码-服务器可能需要安装字体]

 字体:
  - yum install -y mkfontscale
  - yum install -y fontconfig
  - mkdir /usr/share/fonts/chinese
  - cp msyh.ttf /usr/share/fonts/chinese
  - mkfontscale
  - mkfontdir
  - fc-cache -fv
  # 查看是否安装成功
  - fc-list

 phantomjs-2.1.1
  - yum install -y bzip2
  # just do it
  - yum install -y fontconfig freetype libfreetype.so.6
  - tar -jxvf phantomjs-2.1.1-linux-x86_64.tar.bz2 -C /usr/local/
  # 创建软链接
  - ln -s /usr/local/phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs
  # 验证安装是否成功
  - phantomjs --version
  1. 引入maven依赖
<dependency>
   <groupId>com.github.abel533</groupId>
   <artifactId>ECharts</artifactId>
   <version>2.2.7</version>
</dependency>
  1. 后台组织好echart生成图片的option
  2. 调用下面的代码
import com.github.abel533.echarts.Grid;
import com.github.abel533.echarts.Label;
import com.github.abel533.echarts.code.Trigger;
import com.github.abel533.echarts.series.Bar;
import com.github.abel533.echarts.style.ItemStyle;
import com.github.abel533.echarts.style.TextStyle;
import com.github.abel533.echarts.style.itemstyle.Normal;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import com.github.abel533.echarts.axis.AxisLabel;
import com.github.abel533.echarts.axis.CategoryAxis;
import com.github.abel533.echarts.axis.ValueAxis;
import com.github.abel533.echarts.code.Magic;
import com.github.abel533.echarts.code.Tool;
import com.github.abel533.echarts.feature.MagicType;
import com.github.abel533.echarts.json.GsonOption;
import com.github.abel533.echarts.series.Line;

import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 〈〉
 *
 * @author yangyouxing
 * @create 2019-12-18
 * @since 1.0.0
 */
@Slf4j
@Service
public class ImageServiceImpl {

	/**
	 * 调用phantomjs产生图片
	 * @param options       echarts的options
	 * @param imgName       图片名称路径
	 * @param width         1680
	 * @param height        500
	 * @return
	 */
	public String createEchartImage(String options, String imgName, Long width, Long height) {
		String path = "";
		BufferedReader input = null;
		String cmd = "";
		try {
			// 工具类自己搞
			if (OSUtil.isLinux()) {
				path = "/usr/local/download/" + imgName;
			} else {
				path = ResourceUtils.getURL("classpath:download").getPath() + File.separator + imgName;
			}
			File file = new File(path);
			if (file.exists()) {
				file.delete();
			}
			File fileParent = file.getParentFile();
			if(!fileParent.exists()){
				fileParent.mkdirs();
			}
			file.createNewFile();
			String jsPath;
			// 区分服务器和本地环境
			if (OSUtil.isLinux()) {
				jsPath = "/usr/local/echarts/echarts-convert.js";
			} else {
				// resources下创建echarts文件夹
				jsPath = ResourceUtils.getURL("classpath:echarts").getPath() + File.separator + "echarts-convert.js";
			}
			log.info("start cmd exec generate png");
			cmd = "/usr/local/bin/phantomjs " + jsPath + " -options " + options + " -outfile " + path + " -width " + width + " -height " + height;
			log.info("cmd: {}", cmd);
			Process process = Runtime.getRuntime().exec(cmd);
			log.info("end cmd exec generate png");
			input = new BufferedReader(new InputStreamReader(process.getInputStream()));
			String line;
			while ((line = input.readLine()) != null) {
				log.info(line);
			}
			log.info("close");
		} catch (Exception e) {
			e.printStackTrace();
			log.error("echarts error: {}, cmd: {}, options: {}, imageName: {}", e.getMessage(), cmd, options, imgName, e);
		} finally {
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
					e.printStackTrace();
					log.error("关闭流失败, {}, options: {}, imageName: {}", e.getMessage(), options, imgName, e);
				}
			}
		}
		return path;
	}

	/**
	 * 折线图
	 * @param types             y轴值
	 * @param yColor            y轴颜色-多个请自行换类型
	 * @param title             图表标题
	 * @param titleAglin        标题对齐
	 * @param rotate            横坐标旋转角度
	 * @param interval          刻度间隔
	 * @param xDataList         x轴值
	 * @param yDataList         y轴值
	 * @param axisName          横坐标名
	 * @param isHorizontal      是否颠倒x与y轴
	 * @return
	 */
	public String getLineEchartOption(String[] types, String yColor, String title, String titleAglin,
	                                  Integer rotate, Integer interval, List<String> xDataList, List<String> yDataList, String axisName, boolean isHorizontal) {

		GsonOption option = new GsonOption();
		// 大标题、位置
		option.title().text(title).x(titleAglin);

		// 提示工具
		option.tooltip().show(true).trigger(Trigger.axis);

		// 工具栏
		option.toolbox().show(true).feature(Tool.mark, Tool.dataView, new MagicType(Magic.line, Magic.bar), Tool.restore, Tool.saveAsImage);

		// 图例
		for (String legend : types) {
			option.legend(legend);
		}

		// 循环给y轴赋值数据
		for (int i = 0; i < types.length; i++) {
			Line line = new Line();
			String type = types[i];
			line.name(type);
			for (String yy : yDataList) {
				line.data(yy);
			}
			option.series(line);
		}

		// 轴分类
		CategoryAxis x = new CategoryAxis();
		for (String cate : xDataList) {
			x.data(cate);
		}
		x.name(axisName);

		// 设置横坐标旋转角度
		AxisLabel xLabel = new AxisLabel();
		xLabel.setRotate(rotate);
		xLabel.setInterval(interval);
		x.setAxisLabel(xLabel);

		// 横轴为类别、纵轴为值
		if (isHorizontal) {
			// x轴
			option.xAxis(x);

			// y轴
			ValueAxis ecsY = new ValueAxis();
			ecsY.axisLine().lineStyle().color(yColor);
			option.yAxis(ecsY);

		} else {
			// 横轴为值、纵轴为类别
			// x轴
			option.xAxis(new ValueAxis());
			// y轴
			option.yAxis(x);
		}

		Grid grid = new Grid();
		// 控制x轴文字与左边的距离
		grid.setX(70);
		// 控制x2轴文字与右边的距离
		grid.setX2(100);
		// y2可以控制倾斜的文字与底部的距离
		grid.setY2(100);
		// y可以控制倾斜的文字与顶部的距离
		grid.setY(100);
		option.setGrid(grid);

		String optionStr = option.toString().replace(" ", "");
		log.info("line-options: {}", optionStr);
		return optionStr;
	}

	/**
	 * 柱状图
	 * @param types             y轴值
	 * @param typeColors        y轴颜色
	 * @param title             图表标题
	 * @param titleAglin        标题对齐
	 * @param rotate            横坐标旋转角度
	 * @param interval          刻度间隔
	 * @param xDataList         x轴值
	 * @param yDataList         y轴值
	 * @param axisName          横坐标名
	 * @param isHorizontal      是否颠倒x与y轴
	 * @return
	 */
	public String getBarEchartOption(String[] types, String[] typeColors, String title, String titleAglin,
	                                 Integer rotate, Integer interval, String[] xDataList, Integer[] yDataList, String axisName, boolean isHorizontal) {

		GsonOption option = new GsonOption();
		// 大标题、位置
		option.title().text(title).x(titleAglin);

		//显示工具提示,设置提示格式
		option.tooltip().show(true).trigger(Trigger.axis);

		// 工具栏
		option.toolbox().show(true).feature(Tool.mark, Tool.dataView, new MagicType(Magic.line, Magic.bar), Tool.restore, Tool.saveAsImage);

		// 图例
		option.legend(types[0]);

		// 图类别(柱状图)
		Bar bar = new Bar(types[0]);
		int size = xDataList.length;
		if (size > 30) {
			bar.setBarWidth(5);
		} else {
			bar.setBarWidth(30);
		}

		// 循环数据
		for (int i = 0; i < size; i++) {
			String color = typeColors[i % typeColors.length];
			// 类目对应的柱状图
			/**
			 * itemStyle: {
			 * 				normal: {
			 * 						label: {
			 * 								show: true, //开启显示
			 * 								position: 'top', //在上方显示
			 * 								textStyle: { //数值样式
			 * 								color: 'black',
			 * 								fontSize: 16
			 *                              }
			 *                      }
			 *              }
			 * }
			 */
			Label label = new Label();
			label.show(true);
			label.position("top");
			TextStyle textStyle = new TextStyle();
			textStyle.color("black");
			textStyle.fontSize(16);
			label.textStyle(textStyle);

			ItemStyle itemStyle = new ItemStyle();
			Normal normal = new Normal();
			normal.color(color);
			normal.label(label);
			itemStyle.normal(normal);

			Map<String, Object> map = new HashMap<>(16);
			map.put("value", yDataList[i]);
			map.put("itemStyle", itemStyle);
			bar.data(map);
		}

		// 轴分类
		CategoryAxis x = new CategoryAxis();
		for (String cate : xDataList) {
			x.data(cate);
		}

		x.name(axisName);

		// 设置横坐标旋转角度
		AxisLabel xLabel = new AxisLabel();
		xLabel.setRotate(rotate);
		xLabel.setInterval(interval);
		x.setAxisLabel(xLabel);

		// 横轴为类别、纵轴为值
		if (isHorizontal) {
			// x轴
			option.xAxis(x);
			// y轴
			option.yAxis(new ValueAxis());

		} else {
			// 横轴为值、纵轴为类别
			// x轴
			option.xAxis(new ValueAxis());
			// y轴
			option.yAxis(x);
		}
		option.series(bar);

		Grid grid = new Grid();
		// 控制x轴文字与左边的距离
		grid.setX(70);
		// 控制x2轴文字与右边的距离
		grid.setX2(100);
		// y2可以控制倾斜的文字与底部的距离
		grid.setY2(100);
		// y可以控制倾斜的文字与顶部的距离
		grid.setY(100);
		option.setGrid(grid);

		String optionStr = option.toString().replace(" ", "");
		log.info("bar-options: {}", optionStr);
		return optionStr;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值