这次遇到的问题要在发送email时,生成好看的数据图表,所以初步思路就是:java服务端+echars。由于echars是需要用js执行的,所以还需要加入执行环境PhantomJS 。
PhantomJS 是一个基于 WebKit 的服务器端 JavaScript API。原生支持各种Web标准:DOM处理,CSS选择器,JSON,Canvas,和SVG。PhantomJS可以用于页面自动化,网络监测,网页截屏,以及无界面测试等。
第一种方式是前端+echars,生成图表后,后台使用phantomjs进行进行截屏,这个方式我试了可行,但是增加了前端的工作量,丢掉。
下面这种方式,是利用PhantomJS 是一个js执行环境,通过拼凑出cmd的执行命令,把绘图所需要的数据,组装成echars需要的格式一并传入。即通过后台(Java)执行PhantomJS,调用echarts-convert.js+数据,并输出成图片。
①开发环境准备:
去git上下载echartsconvert,本次phantomjs.exe执行的就是echarts-convert.js,它引用了echarts.min.js和jquery-3.2.1.min.js。
下载phantomjs-2.1.1-windows。linux和mac有对应的版本,使用的时候配置phantomjs-2.1.1-windows\bin\phantomjs.exe的路径到cmd命令里就行。
②Demo:
public class EcharsServiceImpl implements EcharsService {
private static final String JSpath = "D:/AAAA/A/echartsconvert/script/echarts-convert.js"; //echarts.js的存放路径
private static final String phantomjs = "D:/AAAA/A/phantomjs-2.1.1-windows/bin/phantomjs.exe"; //PhantomJS的存放路径
public static void main(String[] args) {
String optiona =Constant.dd; //应用预先定义的测试数据,Constant.aa,bb,cc,dd,ee在后面给你们
generateEChart(optiona);
}
public static String generateEChart(String options) {
String dataPath = writeFile(options); //把参数放文件里落地
String path = "D:/AAAA/A/Echart/"+UUID.randomUUID()+".png";
try {
File file = new File(path);
if (!file.exists()) {
File dir = new File(file.getParent());
dir.mkdirs();
file.createNewFile();
}
String blank = " ";
String cmd = phantomjs +blank+ JSpath + " -infile " + dataPath + " -outfile " + file;
Process process = Runtime.getRuntime().exec(cmd);
InputStream inputStream = process.getInputStream();
//System.out.println(inputStream.read()); 看一下流,输出-1就得检查一下前头的cmd了
BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));
//String line = "";
//while ((line = input.readLine()) != null) {
//System.out.println(line);
//}
input.close();
} catch (IOException e) {
e.printStackTrace();
}finally{
return path;
}
}
/**
* 把数据写入文件,调试demo的时候,可以跳过这段,直接指定xxx.json
*/
public static String writeFile(String options) {
String dataPath="D:/AAAA/A/Echart/"+ UUID.randomUUID().toString().substring(0, 8) +".json";
try {
/* 写入Json文件 */
File writename = new File(dataPath); // 相对路径,如果没有则要建立一个新的output.txt文件
if (!writename.exists()) { //文件不存在则创建文件,先创建目录
File dir = new File(writename.getParent());
dir.mkdirs();
writename.createNewFile(); // 创建新文件
}
BufferedWriter out = new BufferedWriter(new FileWriter(writename));
out.write(options);
out.flush(); // 把缓存区内容压入文件
out.close(); // 最后记得关闭文件
} catch (IOException e) {
e.printStackTrace();
}
return dataPath;
}
}
③测试数据准备
测试数据要自己到echars的官网上找(https://echarts.baidu.com/examples/#chart-type-tree),每种图表的option不一样,单击下面不同类型的图进去,组装合适的option。组装的时候要记得修改key-value中value的单引号。
我在下面的常量里准备了饼图、折线图、漏斗图、仪表盘四种简单数据。
/**
* 供echars模拟的数据
* @author PENGJING973
*/
public class Constant {
/**饼图*/
public static final String aa = "{tooltip:{trigger:'item',formatter:'{a} <br/>{b}: {c} ({d}%)'},legend:{orient:'vertical',x:'left',data:['直接访问','邮件营销','联盟广告','视频广告','搜索引擎']},series:[{name:'访问来源',type:'pie',radius:['50%','70%'],avoidLabelOverlap:false,label:{normal:{show:false,position:'center'},emphasis:{show:true,textStyle:{fontSize:'30',fontWeight:'bold'}}},labelLine:{normal:{show:false}},data:[{value:335,name:'直接访问'},{value:310,name:'邮件营销'},{value:234,name:'联盟广告'},{value:135,name:'视频广告'},{value:1548,name:'搜索引擎'}]}]}";
/**折线图 */
public static final String bb = "{\"title\":{\"text\":\"电流图\",\"subtext\":\"电流图\",\"x\":\"left\"},\"toolbox\":{\"feature\":{\"saveAsImage\":{\"show\":true,\"title\":\"保存为图片\",\"type\":\"png\",\"lang\":[\"点击保存\"]}},\"show\":true},\"tooltip\":{\"trigger\":\"axis\"},\"legend\":{\"data\":[\"邮件营销\",\"联盟广告\",\"视频广告\"]},\"xAxis\":[{\"type\":\"category\",\"boundaryGap\":false,\"data\":[\"周一\",\"周二\",\"周三\",\"周四\",\"周五\",\"周六\",\"周日\"]}],\"yAxis\":[{\"type\":\"value\"}],\"series\":[{\"name\":\"邮件营销\",\"type\":\"line\",\"stack\":\"总量\",\"data\":[120,132,101,134,90,230,210]},{\"name\":\"联盟广告\",\"type\":\"line\",\"stack\":\"总量\",\"data\":[220,182,191,234,290,330,310]},{\"name\":\"视频广告\",\"type\":\"line\",\"stack\":\"总量\",\"data\":[150,232,201,154,190,330,410]}]}";
/**漏斗图*/
public static final String cc = "{title:{text:'漏斗图(对比)',subtext:'纯属虚构',left:'left',top:'bottom'},tooltip:{trigger:'item',formatter:'{a} <br/>{b} : {c}%'},toolbox:{show:true,orient:'vertical',top:'center',feature:{dataView:{readOnly:false},restore:{},saveAsImage:{}}},legend:{orient:'vertical',left:'left',data:['产品A','产品B','产品C','产品D','产品E']},calculable:true,series:[{name:'漏斗图',type:'funnel',width:'40%',height:'45%',left:'5%',top:'50%',funnelAlign:'right',center:['25%','25%'],data:[{value:60,name:'产品C'},{value:30,name:'产品D'},{value:10,name:'产品E'},{value:80,name:'产品B'},{value:100,name:'产品A'}]},{name:'金字塔',type:'funnel',width:'40%',height:'45%',left:'5%',top:'5%',sort:'ascending',funnelAlign:'right',center:['25%','75%'],data:[{value:60,name:'产品C'},{value:30,name:'产品D'},{value:10,name:'产品E'},{value:80,name:'产品B'},{value:100,name:'产品A'}]},{name:'漏斗图',type:'funnel',width:'40%',height:'45%',left:'55%',top:'5%',funnelAlign:'left',center:['75%','25%'],data:[{value:60,name:'产品C'},{value:30,name:'产品D'},{value:10,name:'产品E'},{value:80,name:'产品B'},{value:100,name:'产品A'}]},{name:'金字塔',type:'funnel',width:'40%',height:'45%',left:'55%',top:'50%',sort:'ascending',funnelAlign:'left',center:['75%','75%'],data:[{value:60,name:'产品C'},{value:30,name:'产品D'},{value:10,name:'产品E'},{value:80,name:'产品B'},{value:100,name:'产品A'}]}]}";
/**仪表盘*/
public static final String dd ="{tooltip:{formatter:'{a} <br/>{c} {b}'},toolbox:{show:true,feature:{restore:{show:true},saveAsImage:{show:true}}},series:[{name:'速度',type:'gauge',z:3,min:0,max:220,splitNumber:11,radius:'50%',axisLine:{lineStyle:{width:10}},axisTick:{length:15,lineStyle:{color:'auto'}},splitLine:{length:20,lineStyle:{color:'auto'}},axisLabel:{backgroundColor:'auto',borderRadius:2,color:'#eee',padding:3,textShadowBlur:2,textShadowOffsetX:1,textShadowOffsetY:1,textShadowColor:'#222'},title:{fontWeight:'bolder',fontSize:20,fontStyle:'italic'},detail:{formatter:function(value){value=(value+'').split('.');value.length<2&&(value.push('00'));return('00'+value[0]).slice(-2)+'.'+(value[1]+'00').slice(0,2);},fontWeight:'bolder',borderRadius:3,backgroundColor:'#444',borderColor:'#aaa',shadowBlur:5,shadowColor:'#333',shadowOffsetX:0,shadowOffsetY:3,borderWidth:2,textBorderColor:'#000',textBorderWidth:2,textShadowBlur:2,textShadowColor:'#fff',textShadowOffsetX:0,textShadowOffsetY:0,fontFamily:'Arial',width:100,color:'#eee',rich:{}},data:[{value:40,name:'km/h'}]},{name:'转速',type:'gauge',center:['20%','55%'],radius:'35%',min:0,max:7,endAngle:45,splitNumber:7,axisLine:{lineStyle:{width:8}},axisTick:{length:12,lineStyle:{color:'auto'}},splitLine:{length:20,lineStyle:{color:'auto'}},pointer:{width:5},title:{offsetCenter:[0,'-30%'],},detail:{fontWeight:'bolder'},data:[{value:1.5,name:'x1000 r/min'}]},{name:'油表',type:'gauge',center:['77%','50%'],radius:'25%',min:0,max:2,startAngle:135,endAngle:45,splitNumber:2,axisLine:{lineStyle:{width:8}},axisTick:{splitNumber:5,length:10,lineStyle:{color:'auto'}},axisLabel:{formatter:function(v){switch(v+''){case'0':return'E';case'1':return'Gas';case'2':return'F';}}},splitLine:{length:15,lineStyle:{color:'auto'}},pointer:{width:2},title:{show:false},detail:{show:false},data:[{value:0.5,name:'gas'}]},{name:'水表',type:'gauge',center:['77%','50%'],radius:'25%',min:0,max:2,startAngle:315,endAngle:225,splitNumber:2,axisLine:{lineStyle:{width:8}},axisTick:{show:false},axisLabel:{formatter:function(v){switch(v+''){case'0':return'H';case'1':return'Water';case'2':return'C';}}},splitLine:{length:15,lineStyle:{color:'auto'}},pointer:{width:2},title:{show:false},detail:{show:false},data:[{value:0.5,name:'gas'}]}]}";
}
④生成效果
分别修改第二步main方法中optiona 的值,等于Constant.aa,bb,cc,dd,ee,观察到D:\AAAA\A\Echart\文件夹下生成的json文件和png文件。
echars的js参考了这位前辈的csdn:https://blog.csdn.net/leftwukaixing/article/details/89449071,感谢!