Java后台生成ECharts图片,并以Base64字符串返回

前言

 通过echarts的jar包,Java后台生成一张图片,并把图片插入到word中。关于word插图片的代码在下一章。

 需要用到的工具PhantomJS,Echarts-convert.js,jquery.js,echarts.js

1.PhantomJS 介绍

PhantomJS是一个不需要浏览器的富客户端。 

官方介绍:PhantomJS是一个基于 WebKit 的服务器端JavaScript API。它全面支持web而不需浏览器支持,支持各种Web标准:DOM处理,CSS选择器, JSON,Canvas,和SVG。
PhantomJS常用于页面自动化,网络监测,网页截屏,以及无界面测试等。通常我们使用PhantomJS作为爬虫工具。传统的爬虫只能单纯地爬取html的代码,对于js渲染的页面,就无法爬取,如Echarts统计图。而PhantomJS正可以解决此类问题。

 我们可以这么理解PhantomJS,PhantomJS是一个无界面、可运行脚本的谷歌浏览器。

1.1 PhantomJS下载安装

PhantomJS安装非常简单,官网http://phantomjs.org/download.html下载最新的安装包, 安装包有Windows,Mac OS X, Linux 64/32 bit,选择对应的版本下载解压即可使用,在下载包里有个example文件夹,里面对应了许多示例供参考。

为方便使用,我们将phantomjs添加至环境变量中。

windows下操作:

path 下添加  C:\Users\ming\Desktop\java-echarts\phantomjs-2.1.1-windows\bin
打开cmd,查看是否配置成功
C:\Users\ming>phantomjs --version
2.1.1

linux 下操作:

export PHANTOMJS=/usr/local/phantomjs/phantomjs-2.1.1-linux-x86_64
export PATH=$PATH:$PHANTOMJS/bin
# or
export PATH=$PATH:/usr/local/phantomjs/phantomjs-2.1.1-linux-x86_64/bin
# 刷新配置
source /etc/profile

注:linux虽然不需要其他的依赖包,但仍旧需要GLIBCXX_3.4.9和GLIBC_2.7,当然大多数linux是有这两个依赖包的。

 1.2 运行脚本测试

 安装包下 example 文件夹下提供了好多示例,这里以hello.js 为例,在example文件夹下输入 phantomjs hello.js 命令 会输出 Hello, world! 更多用法可参考官方文档。

C:\Users\ming\Desktop\java-echarts\phantomjs-2.1.1-windows\examples>phantomjs hello.js
Hello, world!

 2.Echartsconvert

上面讲述了PhantomJS如何使用,下面我们就从Echarts官网使用JS截图的方式来获取我们想要的图片 

Echartsconvert (Gitee)copy下来代码
注意:因为该源码长期没有更新,script目录下echarts.min.js太过于老旧,无法支持目前Echarts的图形,请大家copy下代码后,更新替换其文件 。最新echarts.min.js下载传送门 

其中echarts-convert.js就是我们要使用到的主C,这个Js就相当于帮我们去Echarts官方运行Dome->生成折线图/柱状图->保存到指定文件夹下

jquery.js自行下载对应版本,我的是3.6.3

附上echarts-converts.js文件


;(function (window, document, undefined) {"use strict";// 引入modulevar system = require('system'), // 获取参数path = phantom.libraryPath,command = require(path + '/module/command.js');// 参数module/*** phantomJs 全局异常监听* @param msg* @param trace*/phantom.onError = function (msg, trace) {var msgStack = ['Convert ERROR: ' + msg];if (trace && trace.length) {msgStack.push('TRACE:');trace.forEach(function (t) {msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function + ')' : ''));});}console.error(msgStack.join('\n'));phantom.exit(1);};/*** 参数* @type {Command}*/var commandParams = command.version('0.0.1').option('-s, --server', 'provide echarts convert http server').option('-p, --port <number>', 'change server port when add -s or --server', 9090).option('-o, --opt <json>', 'add the param of echarts method [ eChart.setOption(opt) ]').option('-t, --type <value>', 'provide file/base64 for image, default file', /^(file|base64)$/i, 'base64').option('-f, --outfile <path>', 'add output of the image file path').option('-w, --width <number>', 'change image width', '600').option('-h, --height <number>', 'change image height', '400').parse(system.args);// ***********************************// Echarts转换器// ***********************************function Convert(params) {this.params = params || commandParams; // 参数命令this.external = {JQUERY3: path + '/script/jquery-3.6.3.min.js',ECHARTS: path + '/script/echarts.min.js',ECHARTS_CHINA: path + '/script/china.js'}; // 外部js}/*** 初始化*/Convert.prototype.init = function () {var params = this.params;this.check(params);if (params.server) {this.server(params);} else {this.client(params);}};/*** 参数检查* @param params*/Convert.prototype.check = function (params) {if (undefined === params.server && undefined === params.opt) {this.error("option argument missing -o, --opt <json>");}if (undefined !== params.opt) {var isJson = this.checkJson(params.opt);if (!isJson) {this.error("--opt <json> args not json string");}}if ('file' === params.type && undefined === params.outfile) {this.createTmpDir();}};/*** 检查是否是json字符串* @param value* @returns {boolean}*/Convert.prototype.checkJson = function (value) {var re = /^\{[\s\S]*\}$|^\[[\s\S]*\]$/;// 类型为stringif (typeof value !== 'string') {return false;}// 正则验证if (!re.test(value)) {return false;}// 是否能解析try {value = "\"" + value + "\"";JSON.parse(value);} catch (err) {return false;}return true;};/*** 创建临时目录,并指定输出路径*/Convert.prototype.createTmpDir = function () {var fs = require('fs'); // 文件操作var tmpDir = fs.workingDirectory + '/tmp';// 临时目录是否存在且可写if (!fs.exists(tmpDir)) {if (!fs.makeDirectory(tmpDir)) {this.error('Cannot make ' + tmpDir + ' directory\n');}}this.params.outfile = tmpDir + "/" + new Date().getTime() + ".png";};/*** 服务* @param params*/Convert.prototype.server = function (params) {var server = require('webserver').create(), // 服务端convert = this;var listen = server.listen(params.port, function (request, response) {/*** 输出* @param data* @param success*/function write(data, success, msg) {response.statusCode = 200;response.headers = {'Cache': 'no-cache','Content-Type': 'application/json;charset=utf-8'};response.write(convert.serverResult(data, success, msg));response.close();}//获取参数var args = convert.serverGetArgs(request);if (args.opt !== undefined) {var check = convert.serverCheckAndSet(params, args);if (check) {convert.client(params, write);} else {write("", false, "failed to get image, please check parameter [opt] is a JSON");}} else {write("", false, "failed to get image, missing parameter [opt]");}});// 判断服务是否启动成功if (!listen) {this.error("could not create echarts-convert server listening on port " + params.port);} else {console.log("echarts-convert server start success. [pid]=" + system.pid);}};/*** 服务参数检查和赋值* @param params* @param args* @returns {boolean}*/Convert.prototype.serverCheckAndSet = function (params, args) {if (this.checkJson(args.opt)) {params.opt = args.opt;} else {return false;}if (/^(file|base64)$/i.exec(args.type)) {params.type = args.type;}if (!isNaN(args.width)) {params.width = args.width;}if (!isNaN(args.height)) {params.height = args.height;}return true;};/*** 结果返回* @param data* @param success* @param msg*/Convert.prototype.serverResult = function (data, success, msg) {var result = {code: success ? 1 : 0,msg: undefined === msg ? success ? "success" : "failure" : msg,data: data};return JSON.stringify(result);};/*** 获取参数* @param request* @returns {{}}*/Convert.prototype.serverGetArgs = function (request) {var args = {};if ('GET' === request.method) {var index = request.url.indexOf('?');if (index !== -1) {var getQuery = request.url.substr(index + 1);args = this.serverParseArgs(getQuery);}} else if ('POST' === request.method) {var postQuery = request.post;args = this.serverParseArgs(postQuery);}return args;};/*** 解析参数* @param query 字符串* @returns {{}} 对象*/Convert.prototype.serverParseArgs = function (query) {var args = {},pairs = query.split("&");for (var i = 0; i < pairs.length; i++) {var pos = pairs[i].indexOf('=');if (pos === -1)continue;var key = pairs[i].substring(0, pos);var value = pairs[i].substring(pos + 1);// 中文解码,必须写两层value = decodeURIComponent(decodeURIComponent(value));args[key] = value;}return args;};/*** 访问渲染* @param params* @param fn*/Convert.prototype.client = function (params, fn) {var page = require('webpage').create(); // 客户端var convert = this,external = this.external,render,output;/***  渲染* @returns {*}*/render = function () {switch (params.type) {case 'file':// 渲染图片page.render(params.outfile);return params.outfile;case 'base64':default:var base64 = page.renderBase64('PNG');return base64;}};/*** 输出* @param content 内容* @param success 是否成功*/output = function (content, success, msg) {if (params.server) {fn(content, success, msg);page.close();} else {console.log(success ? "[SUCCESS]:" : "[ERROR]:" + content);page.close();convert.exit(params);// exit}};/*** 页面console监听* @param msg* @param lineNum* @param sourceId*/page.onConsoleMessage = function (msg, lineNum, sourceId) {console.log(msg);};/*** 页面错误监听* @param msg* @param trace*/page.onError = function (msg, trace) {output("", false, msg); // 失败,返回错误信息};// 空白页page.open("about:blank", function (status) {// 注入依赖js包var hasJquery = page.injectJs(external.JQUERY3);var hasEchart = page.injectJs(external.ECHARTS);var hasEchartChina = page.injectJs(external.ECHARTS_CHINA);// 检查js是否引用成功if (!hasJquery && !hasEchart) {output("Could not found " + external.JQUERY3 + " or " + external.ECHARTS, false);}// 创建echartspage.evaluate(createEchartsDom, params);// 定义剪切范围,如果定义则截取全屏page.clipRect = {top: 0,left: 0,width: params.width,height: params.height};// 渲染var result = render();// 成功输出,返回图片或其他信息output(result, true);});};/*** 创建eCharts Dom层* @param params 参数*/function createEchartsDom(params) {// 动态加载js,获取options数据$('<script>').attr('type', 'text/javascript').html('var options = ' + params.opt).appendTo(document.head);// 取消动画,否则生成图片过快,会出现无数据if (options !== undefined) {options.animation = false;}// body背景设置为白色$(document.body).css('backgroundColor', 'white');// echarts容器var container = $("<div>").attr('id', 'container').css({width: params.width,height: params.height}).appendTo(document.body);var eChart = echarts.init(container[0]);eChart.setOption(options);}/*** debug,将对象转成json对象* @param obj*/Convert.prototype.debug = function (obj) {console.log(JSON.stringify(obj, null, 4));};/*** 错误信息打印并退出* @param str 错误信息*/Convert.prototype.error = function (str) {console.error("Error:" + str);this.exit();};/*** 退出,参数为空或是server时,不退出* @param params 参数*/Convert.prototype.exit = function (params) {if (undefined === params || undefined === params.server) {phantom.exit();}};// 构建,入口new Convert(commandParams).init();}(this, this.document));

 结构如下:

 2.1 启动命令

windows下启动命令 端口默认 9090

C:\Users\ming\Desktop\java-echarts\echartsconvert>phantomjs echarts-convert.js -s

 linux 下启动命令 端口默认 9090

nohup phantomjs /usr/local/phantomjs/echartsconvert/echarts-convert.js -s &

 2.2 中文字体乱码问题

 linux 下 phantonjs 没有支持的中文, 需要进行安装对应包。也可安装其它字体库,这里不做更多叙述。

此处执行linux字符集安装即可
在centos中执行:yum install bitmap-fonts bitmap-fonts-cjk
在ubuntu中执行:sudo apt-get install xfonts-wqy

 2.3 饼图无法生成,Null异常等问题

饼图绘制不了,request时就卡住的问题
solution1:此处并不是饼图绘制不了,而是只要opt中含有'%'都会挂,原因是作者在代码里执行了两次decodeURIComponent(详情参考echarts-convert.js源码259行),所以'%'传递时也必需encode两次,否则会造成%后的json串无法被decode导致卡住的问题。
此处可以将'%'替换为'%25'解决,或是改源码将decodeURIComponent改为一次,暂时没有发现改为一次decode会出现中文问题

3.JSON格式数据生成图表图片

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
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;import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 以 EchartsData 示例中 getBar() 数据为例,将生成的Echarts数据转为base64图片** @author demain_lee* @since  2022/12/28*/
public class EchartData2Base64 {public static void main(String[] args) throws IOException {// json 格式数据String jsonData = "{"xAxis":[{"type":"category","data":["Matcha Latte","Milk Tea","Cheese Cocoa","Walnut Brownie"]}],"yAxis":[{"type":"value"}],"tooltip":{"trigger":"item"},"legend":{},"series":[{"type":"bar","name":"2020","data":[43.3,83.1,86.4,72.4]},{"type":"bar","name":"2021","data":[85.8,73.4,65.2,53.9]},{"type":"bar","name":"2022","data":[93.7,55.1,82.5,39.1]}]}";// PhantomJS 服务器地址String url = "http://localhost:9090";Map<String, String> map = new HashMap<>();//此处已将%处理为%25jsonData = jsonData.replaceAll("\\s+", "").replaceAll("\"", "'").replaceAll("%", "%25");map.put("opt", jsonData);String resultData = post(url, map, "utf-8");System.out.println(resultData);}public static String post(String url, Map<String, String> map, String encoding) throws IOException {String body = "";// 创建httpclient对象CloseableHttpClient client = HttpClients.createDefault();// 创建post方式请求对象HttpPost httpPost = new HttpPost(url);// 装填参数List<NameValuePair> nvp = new ArrayList<>();if (map != null) {for (Map.Entry<String, String> entry : map.entrySet()) {nvp.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));}}// 设置参数到请求对象中httpPost.setEntity(new UrlEncodedFormEntity(nvp, encoding));// 执行请求操作,并拿到结果(同步阻塞)CloseableHttpResponse response = client.execute(httpPost);// 获取结果实体HttpEntity entity = response.getEntity();if (entity != null) {// 按指定编码转换结果实体为String类型body = EntityUtils.toString(entity, encoding);}EntityUtils.consume(entity);// napsresponse.close();return body;}
}

3.1 base64格式数据

resultData 返回的数据包含base64格式数据 

//返回数据
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

3.2在线查看Base64图片

Base64/图片转换 - 在线工具 (try8.cn)

 注意添加data:image/png;base64,

 3.3在线修改图表样式

Examples - Apache ECharts

 3.4在线将opt转换为Json

 在线JS对象转JSON工具 - UU在线工具 (uutool.cn)

注意只需要将opt的内容进行转换,不需要带上option=; 

4.结束语

本篇文章到这里就结束了,本文介绍了如何通过Java 第三方库去处理对应的图表数据,以及通过基于PhantomJS的第三方开源项目echartsconvert进行数据转换,获取最后需要的Base64格式的图片数据。有了这个数据可以把它运用到自己需要的地方。比如,写到Word或PDF文档中

将Base64图片通过POI插入Word中 

Echarts图表Java后端生成Base64图片格式,POI写入Base64图片到Word中_青冘的博客-CSDN博客 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/27931.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[保研/考研机试] 猫狗收容所 C++实现

题目描述&#xff1a; 输入&#xff1a; 第一个是n&#xff0c;它代表操作序列的次数。接下来是n行&#xff0c;每行有两个值m和t&#xff0c;分别代表题目中操作的两个元素。 输出&#xff1a; 按顺序输出收养动物的序列&#xff0c;编号之间以空格间隔。 源代码&#xff…

用i18n 实现vue2+element UI的国际化多语言切换详细步骤及代码

一、i18n的安装 这个地方要注意自己的vue版本和i1n8的匹配程度&#xff0c;如果是vue2点几&#xff0c;记得安装i18n的8版本&#xff0c;不然会自动安装的最新版本&#xff0c;后面会报错哦&#xff0c;查询了下资料&#xff0c;好像最新版本是适配的vue3。 npm install vue-…

Attacks in NLP

一、 Introduction NLP对抗攻击是人工智能对抗攻击的一个重要的组成部分&#xff0c;但是最近几年才逐渐开始兴起&#xff0c;究其原因在于NLP对抗攻击与传统computer vision或者audio对抗攻击有很大的不同&#xff0c;主要在于值空间的连续性&#xff08;CV、audio&#xff0…

04-6_Qt 5.9 C++开发指南_QListWidget和QToolButton

文章目录 1. 实例简介2. 源码2.1 混合式界面设计2.2 mainwindow.h2.3 mainwindow.cpp 1. 实例简介 Qt 中用于项 (Item)处理的组件有两类&#xff0c;一类是 Item Views&#xff0c;包括 QListView、QTreeView、QTableView、QColumnView 等;另一类是 Item Widgets&#xff0c;包…

C# 完成串口通信RS485

C# 完成串口通信RS485|RS232上下位机交互 第零步&#xff1a; 我用的是电脑usb 转串口的所以首先是驱动程序下载&#xff0c;我们用的是CH341 下载地址&#xff1a;https://www.wch.cn/downloads/CH341SER_EXE.html 第一步&#xff1a;连接机器 RS485 上面有三个端子&#xf…

深度学习与计算机相结合:直播实时美颜SDK的创新之路

时下&#xff0c;实时美颜技术就成为了直播主们的得力工具&#xff0c;它可以在直播过程中即时处理视频画面。而支持实时美颜功能的SDK更是推动了这项技术的发展&#xff0c;让直播主和普通用户都能轻松使用美颜功能。 一、美颜技术的演进 早期的美颜技术主要依赖于简单的图…

eclipse Java Editor Templates

​ Window - Preferences - Java - Editor - Templates ​ date ${currentDate:date(yyyy.MM.dd)}

Java版工程行业管理系统源码-专业的工程管理软件-em提供一站式服务

​ Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目…

塔矢行洋对战藤原佐为,谁才是最接近神之一手的人

大家好, 我是嘉宾, 今天我们来盘点一下古今第一高手对局 &#xff0c;塔矢行洋对战藤原佐为&#xff0c;谁才是最接近神之一手的人&#xff0c; 在所有设定都点击好之后, 塔矢行洋下出了自己的第一步 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 佐…

【每日一题】—— B. Maximum Rounding(Codeforces Round 891 (Div. 3))

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

VIM 编辑器: Bram Moolenaar

VIM 用了很长时间&#xff0c; 个人的 VIM 配置文件差不多10年没有更新了。以前写程序的时候&#xff0c; 编辑都用这个。 linux kernel&#xff0c; boost规模的代码都不在话下。现在虽然代码写的少了&#xff0c;依然是我打开文件的首选。 现在用手机了&#xff0c;配个蓝牙键…

无涯教程-Perl - endnetent函数

描述 此功能告诉系统您不再希望使用getnetent从网络列表中读取条目。 语法 以下是此函数的简单语法- endnetent返回值 此函数不返回任何值。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perluse Socket;while ( ($name, $aliases, $addrtype, $net) getnetent() )…

Android 9-- 源码角度: Home键的监听和拦截

在做应用层APP需求的过程中&#xff0c;HOME键的监听&#xff0c;Back键的监听&#xff0c;这都是很常见的问题&#xff0c;那你有试过&#xff0c;去拦截HOME键的事件吗&#xff0c;有去了解过如何处理吗&#xff0c;流程如何 首先大家应该先了解一种情况&#xff0c;就是Andr…

Linux tun虚拟网卡通信初识

什么是linux tun设备 Linux TUN 设备是一种虚拟网络设备&#xff0c;用于在用户空间和内核空间之间建立数据通道&#xff0c;使用户空间程序可以通过这个设备与内核网络栈进行交互。TUN 设备是一种通用的网络隧道设备&#xff0c;常用于实现虚拟专用网络&#xff08;VPN&#…

【湍流介质的三维传播模拟器】全衍射3-D传播模拟器,用于在具有随机和背景结构的介质中传播无线电和光传播(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

华为OD机试(含B卷)真题2023 算法分类版,58道20个算法分类,如果距离机考时间不多了,就看这个吧,稳稳的

目录 一、数据结构1、线性表2、优先队列3、滑动窗口4、二叉树5、并查集6、栈 二、算法1、基础算法2、字符串3、图4、动态规划5、数学 三、漫画算法2&#xff1a;小灰的算法进阶参与方式 很多小伙伴问我&#xff0c;华为OD机试算法题太多了&#xff0c;知识点繁杂&#xff0c;如…

中文版开源Llama 2同时有了语言、多模态大模型,完全可商用

可以说&#xff0c;AI 初创公司 LinkSoul.Al 的这些开源项目让海外开源大模型在国内的普及和推广速度与国际几乎保持了一致。 7 月 19 日&#xff0c;Meta 终于发布了免费可商用版本 Llama 2&#xff0c;让开源大模型领域的格局发生了巨大变化。 Llama 2 模型系列包含 70 亿、…

Python识别抖音Tiktok、巨量引擎滑块验证码识别

由于最近比较忙&#xff0c;所以本周搞了一个相对简单的验证码&#xff0c;就是抖音Tiktok的滑块验证码&#xff0c;这也是接到客户的一个需求。这种验证码通常在电脑端登录抖音、巨量引擎的的时候出现。 首先看一下最终的效果&#xff1a; 验证码识别过程 1、利用爬虫采集图…

查看单元测试用例覆盖率新姿势:IDEA 集成 JaCoCo

1、什么是 IDEA IDEA 全称 IntelliJ IDEA&#xff0c;是 Java 编程语言开发的集成环境。IntelliJ 在业界被公认为最好的 Java 开发工具&#xff0c;尤其在智能代码助手、代码自动提示、重构、JavaEE 支持、各类版本工具(git、SVN 等)、JUnit、CVS 整合、代码分析、 创新的 GUI…

04-5_Qt 5.9 C++开发指南_QComboBox和QPlainTextEdit

文章目录 1. 实例功能概述2. 源码2.1 可视化UI设计2.2 widget.h2.3 widget.cpp 1. 实例功能概述 QComboBox 是下拉列表框组件类&#xff0c;它提供一个下拉列表供用户选择&#xff0c;也可以直接当作一个QLineEdit 用作输入。OComboBox 除了显示可见下拉列表外&#xff0c;每个…