数据统计图表生成方案调研
一、背景
项目需要生成并推送数据统计图表,主要为折线图等常见类型的数据可视化图形。
二、技术选型
起初考虑使用jfeechart实现,但是参考案例效果图,直接放弃;
第二种方式是使用python的matplotlib包实现,这种方式需要在java代码中调用python程序来实现,且生成的图片并不美观;
最后,参考gitee上的案例,选择echartsconvert来实现,该项目选择echarts数据可视化图标库来生成图片,比较美观。且是独立部署的项目,减少与Java服务的耦合。
三、部署实践
1、安装phantomjs
官网下载phantomjs.org/download.ht…
下载解压后,放到指定目录,并配置环境变量;
2、运行echartsconvert服务
原项目已经很久不维护,可将echarts.min.js替换为最新版本
使用phantomjs运行EChartConvert,并指定端口
phantomjs echarts-convert.js -s -p 8090
3、使用模板生成echarts图表
- 创建FreeMarker模板文件
option.ftl
{
title: {
text: '周阅读人数'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['男', '女']
},
xAxis: [{
type: 'category',
boundaryGap: false,
data: $ {
dateArray
},
axisLabel: {
interval: 0,
rotate: 30,
}
}],
yAxis: [{
type: 'value'
}],
series: [{
name: '男',
type: 'line',
data: $ {
dataArray1
}
},
{
'name': '女',
'type': 'line',
data: $ {
dataArray2
}
}
]
}
注意:
使用freemarker模板引擎,方便动态传输数据;
上述模板为echarts折线图堆叠,使用时可以在echarts官网选择合适的类型并在线调试,然后复制修改为ftl文件,将需要动态添加的数据设为freemarker插值;
- 构建请求参数,调用接口生成图片
public class EchartsUtil {
private static String url = "http://localhost:8090";
private static final String SUCCESS_CODE = "1";
public static String generateEchartsBase64(String option) {
String base64 = "";
if (option == null) {
return base64;
}
option = option.replaceAll("\s+", "").replaceAll(""", "'");
// 将option字符串作为参数发送给echartsConvert服务器
Map<String, Object> params = new HashMap<>();
params.put("opt", option);
//调用echartsconvert接口,生成图片,返回base64编码
String response = HttpUtil.post(url, params);
// 解析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;
}
}
- 封装FreemarkerUtil工具类,用于合并数据模型和ftl模板
public class FreemarkerUtil {
private static final String path = FreemarkerUtil.class.getClassLoader().getResource("").getPath();
public static String generateString(String templateFileName, String templateDirectory, Map<String, Object> datas)
throws IOException, TemplateException {
//创建Configuration实例
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
// 设置默认编码
cfg.setDefaultEncoding("UTF-8");
// 设置模板所在文件夹
cfg.setDirectoryForTemplateLoading(new File(path + templateDirectory));
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
// 获取模板
Template template = cfg.getTemplate(templateFileName);
// 将数据模型与模板合并
try (StringWriter stringWriter = new StringWriter()) {
template.process(datas, stringWriter);
stringWriter.flush();
return stringWriter.getBuffer().toString();
}
}
}
- 构建指标数据,调用工具方法,获取base64编码,并生成图片
public static void main(String[] args) throws TemplateException, IOException {
// 变量
String[] dateArray = {"20221128", "20221205", "20221212", "20221219", "20221226", "20230102", "20230109"};
Object[] dataArray1 = {300, 400, 500, 672, 736, 900, 743};
Object[] dataArray2 = {400, 465, 788, 298, 353, 520, 673};
// 模板参数
HashMap<String, Object> datas = new HashMap<>();
datas.put("dateArray", JSON.toJSONString(dateArray));
datas.put("dataArray1", JSON.toJSONString(dataArray1));
datas.put("dataArray2", JSON.toJSONString(dataArray2));
// 生成option字符串
String option = FreemarkerUtil.generateString("option.ftl", "template/echarts", datas);
System.out.println(option);
// 根据option参数
String base64 = EchartsUtil.generateEchartsBase64(option);
System.out.println("BASE64:" + base64);
generateImage(base64, "/data/image/test.png");
}
//根据返回的base64编码生成图片
public static void generateImage(String base64, String path) throws IOException {
BASE64Decoder decoder = new BASE64Decoder();
try (OutputStream out = new FileOutputStream(path)) {
// 解密
byte[] b = decoder.decodeBuffer(base64);
for (int i = 0; i < b.length; ++i) {
if (b[i] < 0) {
b[i] += 256;
}
}
out.write(b);
out.flush();
}
}
最终生成趋势图如下: