很多时候需要自动生成一些文档,并在文档中插入一些图片,一般B/S系统中图片由Echarts生成,但文档在java后台生成,这时怎样在后台调用Echarts的功能呢?答案是phantomJS。
文章目录
1.安装phantomJS
下载地址:https://bitbucket.org/ariya/phantomjs/downloads/,国内镜像地址:http://npm.taobao.org/dist/phantomjs/
有各种版本的,我这里以windows为例。
2.下载EchartsConvert
下载地址:https://gitee.com/saintlee/echartsconvert
3.运行phantomJS
可以使用两种方式生成图片,直接在本地调用命令行或者使用远程服务,下面以远程服务为例。
在phantomJS解压后的bin目录中通过命令行运行phantomjs.exe <echarts-convert的路径> -s -p <端口>
如下图所示:
4.引入依赖
通过maven或gradle引入httpclient、freemark和fastjson的依赖,我这里略过了
5.httpclient工具类
package com.iscas.base.biz.util.echarts;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
public class HttpUtil {
public static String post(String url, Map<String, String> params, String charset)
throws ClientProtocolException, IOException {
String responseEntity = "";
// 创建CloseableHttpClient对象
CloseableHttpClient client = HttpClients.createDefault();
// 创建post方式请求对象
HttpPost httpPost = new HttpPost(url);
// 生成请求参数
List<NameValuePair> nameValuePairs = new ArrayList<>();
if (params != null) {
for (Entry<String, String> entry : params.entrySet()) {
nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
}
// 将参数添加到post请求中
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, charset));
// 发送请求,获取结果(同步阻塞)
CloseableHttpResponse response = client.execute(httpPost);
// 获取响应实体
HttpEntity entity = response.getEntity();
if (entity != null) {
// 按指定编码转换结果实体为String类型
responseEntity = EntityUtils.toString(entity, charset);
}
// 释放资源
EntityUtils.consume(entity);
response.close();
return responseEntity;
}
}
6.freemarker工具类
package com.iscas.base.biz.util.echarts;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.lang3.StringUtils;
/**
* freemarker工具类
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/11/24
* @since jdk1.8
*/
public class FreemarkerUtils {
public static String generateString(String templateFileName, String templateDirectoryPath, Map<String, Object> datas)
throws IOException, TemplateException {
Configuration configuration = new Configuration(Configuration.VERSION_2_3_0);
// 设置默认编码
configuration.setDefaultEncoding("UTF-8");
if (templateDirectoryPath.startsWith("classpath:")) {
//设置类加载器
configuration.setClassLoaderForTemplateLoading(Thread.currentThread().getContextClassLoader(),
StringUtils.substringAfter(templateDirectoryPath, "classpath:"));
} else {
// 设置模板所在文件夹
configuration.setDirectoryForTemplateLoading(new File(templateDirectoryPath));
}
// 生成模板对象
Template template = configuration.getTemplate(templateFileName);
// 将datas写入模板并返回
try (StringWriter stringWriter = new StringWriter()) {
template.process(datas, stringWriter);
stringWriter.flush();
return stringWriter.getBuffer().toString();
}
}
}
7.图像工具类
public class ImageUtils {
private ImageUtils() {
}
/**
* 将base64转为图片
* */
public static void convertBase64ToImage(String base64, OutputStream os) throws IOException {
Base64.Decoder decoder = Base64.getDecoder();
// 解密
byte[] b = decoder.decode(base64);
for (int i = 0; i < b.length; ++i) {
if (b[i] < 0) {
b[i] += 256;
}
}
os.write(b);
os.flush();
}
}
8.Echarts工具类
package com.iscas.base.biz.util.echarts;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.client.ClientProtocolException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 参考:https://www.jianshu.com/p/dfc28fd7d786
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/11/24 21:00
* @since jdk1.8
*/
public class EchartsUtils {
private static final String SUCCESS_CODE = "1";
public static String generateEchartsBase64(String phantomjsUrl, String option) throws ClientProtocolException, IOException {
String base64 = "";
if (option == null) {
return base64;
}
option = option.replaceAll("\\s+", "").replaceAll("\"", "'");
// 将option字符串作为参数发送给echartsConvert服务器
Map<String, String> params = new HashMap<>();
params.put("opt", option);
String response = HttpUtil.post(phantomjsUrl, params, "utf-8");
// 解析echartsConvert响应
JSONObject responseJson = JSON.parseObject(response);
String code = responseJson.getString("code");
// 如果echartsConvert正常返回
if (SUCCESS_CODE.equals(code)) {
base64 = responseJson.getString("data");
}
// 未正常返回
else {
String string = responseJson.getString("msg");
throw new RuntimeException(string);
}
return base64;
}
}
9.模板opt.ftl
{
title: {
text:'${title}',
x:'middle',
textAlign:'center'
},
xAxis: {
type: 'category',
data: ${categories}
},
yAxis: {
type: 'value'
},
series: [{
data: ${values},
type: 'bar'
}]
}
10.测试
public class EchartTests {
private static String url = "http://localhost:7777";
@Test
public void test() throws TemplateException, IOException {
// 变量
String title = "销量";
String[] categories = new String[] { "2019", "2020", "2021" };
int[] values = new int[] { 100, 105, 200 };
// 模板参数
HashMap<String, Object> datas = new HashMap<>();
datas.put("categories", JSON.toJSONString(categories));
datas.put("values", JSON.toJSONString(values));
datas.put("title", title);
// 生成option字符串
// String option = FreemarkerUtils.generateString("option.ftl", "C:\\ideaProjects\\newframe\\biz-base\\src\\test\\java\\com\\iscas\\base\\biz\\util\\echarts", datas);
String option = FreemarkerUtils.generateString("option.ftl", "classpath:ftl", datas);
// 根据option参数
String base64 = EchartsUtils.generateEchartsBase64(url, option);
System.out.println("BASE64:" + base64);
File test = File.createTempFile("test", ".png");
test.deleteOnExit();
try (OutputStream os = new FileOutputStream(test)) {
ImageUtils.convertBase64ToImage(base64, os);
}
System.out.println("测试完成");
}
}
11.生成的图片
12.生成折线图
上面是一个生成生成简单柱状图的例子,下面我们自定义一个折线图模板,生成一个折线图
折线图模板line.ftl
{
"title": {
"text": "${title}",
"x": "left"
},
"tooltip": {
"trigger": "axis"
},
"legend": {
"data": ${legendData}
},
"xAxis": [
{
"boundaryGap": false,
"type": "category",
"data": ${xAxisData}
}
],
"yAxis": ${yAxis},
"series": ${data}
}
测试:
//测试折线图
@Test
public void testLine() throws TemplateException, IOException {
// 标题
String title = "资源增长情况";
//legendData
String[] legendData = new String[]{"ECS", "实例", "CPU", "MEM"};
//xAxisData
String[] xAxisData = new String[]{"2021-10-01", "2021-10-02", "2021-10-03", "2021-10-04", "2021-10-05"};
//yAxis
ArrayList<Map<String, Object>> yAxis = new ArrayList<>(){{
add(new HashMap<>(){{
put("type", "value");
put("position", "left");
put("name", "台");
}});
add(new HashMap<>(){{
put("type", "value");
put("position", "right");
put("name", "使用量");
}});
}};
//seriesData
ArrayList<Map<String, Object>> series = new ArrayList<>(){{
add(new HashMap<>(){{
put("name", "ECS");
put("type", "line");
put("data", new int[]{100, 200, 210, 180, 260});
}});
add(new HashMap<>(){{
put("name", "实例");
put("type", "line");
put("data", new int[]{320, 220, 268, 320, 330});
}});
add(new HashMap<>(){{
put("name", "CPU");
put("type", "line");
put("data", new int[]{120, 110, 121, 110, 160});
}});
add(new HashMap<>(){{
put("name", "MEM");
put("type", "line");
put("data", new int[]{320, 23, 65, 190, 285});
}});
}};
// 模板参数
HashMap<String, Object> datas = new HashMap<>();
datas.put("xAxisData", JSON.toJSONString(xAxisData));
datas.put("legendData", JSON.toJSONString(legendData));
datas.put("title", title);
datas.put("yAxis", JSON.toJSONString(yAxis));
datas.put("data", JSON.toJSONString(series));
// 生成option字符串
String option = FreemarkerUtils.generateString("line.ftl", "classpath:ftl", datas);
// 根据option参数
// option = "{\"title\":{\"text\":\"资源增长情况\",\"x\":\"left\"},\"toolbox\":{\"feature\":{\"saveAsImage\":{\"show\":true,\"title\":\"保存为图片\",\"type\":\"png\",\"lang\":[\"点击保存\"]}},\"show\":true},\"tooltip\":{\"trigger\":\"axis\"},\"legend\":{\"data\":[\"ECS\",\"实例\",\"CPU\",\"MEM\"]},\"xAxis\":[{\"boundaryGap\":false,\"type\":\"category\",\"data\":[\"2019-03-09\",\"2019-03-02\",\"2019-03-16\"]}],\"yAxis\":[{\"type\":\"value\",\"position\":\"left\",\"name\":\"ECS台\",\"axisLine\":{\"lineStyle\":{\"color\":\"#1E90FF\"}}},{\"type\":\"value\",\"position\":\"left\",\"name\":\"容器实例台\",\"axisLine\":{\"lineStyle\":{\"color\":\"#8A2BE2\"}}}],\"series\":[{\"name\":\"ECS\",\"type\":\"line\",\"stack\":\"总量\",\"data\":[120,132,101,134,90,230,210]},{\"name\":\"实例\",\"type\":\"line\",\"stack\":\"总量\",\"data\":[220,182,191,234,290,330,310]},{\"name\":\"CPU\",\"type\":\"line\",\"stack\":\"总量\",\"data\":[150,232,201,154,190,330,410]},{\"name\":\"MEM\",\"type\":\"line\",\"stack\":\"总量\",\"data\":[150,232,201,154,190,330,410]}]}" ;
String base64 = EchartsUtils.generateEchartsBase64(url, option);
System.out.println("BASE64:" + base64);
File test = File.createTempFile("test", ".png");
test.deleteOnExit();
try (OutputStream os = new FileOutputStream(test)) {
ImageUtils.convertBase64ToImage(base64, os);
}
System.out.println("测试完成");
}
生成的图片:
13.Java使用Echarts工具生成option
除了上面的使用freemarker的方式生成option,还有一种方式是使用Echarts工具生成。
(1)首先引入Echarts依赖
gradle:
compile group: 'com.github.abel533', name: 'ECharts', version: '3.0.0.6'
maven:
<dependency>
<groupId>com.github.abel533</groupId>
<artifactId>ECharts</artifactId>
<version>3.0.0.6</version>
</dependency>
(2)生成option
生成时可以照着echarts官网的js来生成,基本是一一对应的
private String createPie() {
List<Map<String, Object>> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Map<String, Object> map = new HashMap<>();
map.put("NAME", "张三" + i);
map.put("TOTAL", new Random().nextInt(50));
list.add(map);
}
//创建Option
Option option = new GsonOption();
option.title(new Title().text("测试饼图").x("middle"));
option.tooltip(Trigger.item);
option.legend(new Legend().orient(Orient.vertical).left("left"));
//饼图数据
Pie pie = new Pie("测试饼图");
//循环数据
for (Map<String, Object> objectMap : list) {
//饼图数据
pie.data(new PieData(objectMap.get("NAME").toString(), objectMap.get("TOTAL")));
}
//设置数据
option.series(pie);
String optionStr = option.toString().replace(" ", "");
return optionStr;
}
(3)生成图片
//测试饼状图
@Test
public void testPie() throws IOException {
String option = createPie();
String base64 = EchartsUtils.generateEchartsBase64(url, option);
System.out.println("BASE64:" + base64);
File test = File.createTempFile("test", ".png");
test.deleteOnExit();
try (OutputStream os = new FileOutputStream(test)) {
ImageUtils.convertBase64ToImage(base64, os);
}
System.out.println("测试完成");
}
(4) 生成的图片
版权声明:本文为u011943534原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。