当前位置:   article > 正文

SptingBoot基于Echarts生成图表,后端生成ECharts图片_failed to get image, please check parameter [opt]

failed to get image, please check parameter [opt] is a json

前言

         近期有一个生成PDF数据报的需求,PDF中需要生成折线图,饼图,环形图,柱状图等需求以及一些表格信息的东西,需要后台定期生成报表,并写入PDF中。

        大概流程是通过echarts,Java后台生成一张图片,并把图片插入到word中,再将word转换为pdf。

        关于word插图片的代码在下一章。

 此章需要用到的工具如下(都已提供):
PhantomJS,Echarts-convert.js,jquery.js,echarts.js

效果图片

1.PhantomJS 介绍

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

  1. 官方介绍:PhantomJS是一个基于 WebKit 的服务器端JavaScript API。它全面支持web而不需浏览器支持,支持各种Web标准:DOM处理,CSS选择器, JSONCanvas,和SVG
  2. PhantomJS常用于页面自动化,网络监测,网页截屏,以及无界面测试等。
  3. 通常我们使用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下操作:

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

linux 下操作:

  1. export PHANTOMJS=/usr/local/phantomjs/phantomjs-2.1.1-linux-x86_64
  2. export PATH=$PATH:$PHANTOMJS/bin
  3. # or
  4. export PATH=$PATH:/usr/local/phantomjs/phantomjs-2.1.1-linux-x86_64/bin
  5. # 刷新配置
  6. source /etc/profile

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

 1.2 运行脚本测试

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

  1. C:\Users\ming\Desktop\java-echarts\phantomjs-2.1.1-windows\examples>phantomjs hello.js
  2. 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文件

  1. ;(function (window, document, undefined) {
  2. "use strict";
  3. // 引入module
  4. var system = require('system'), // 获取参数
  5. path = phantom.libraryPath,
  6. command = require(path + '/module/command.js');// 参数module
  7. /**
  8. * phantomJs 全局异常监听
  9. * @param msg
  10. * @param trace
  11. */
  12. phantom.onError = function (msg, trace) {
  13. var msgStack = ['Convert ERROR: ' + msg];
  14. if (trace && trace.length) {
  15. msgStack.push('TRACE:');
  16. trace.forEach(function (t) {
  17. msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function + ')' : ''));
  18. });
  19. }
  20. console.error(msgStack.join('\n'));
  21. phantom.exit(1);
  22. };
  23. /**
  24. * 参数
  25. * @type {Command}
  26. */
  27. var commandParams = command
  28. .version('0.0.1')
  29. .option('-s, --server', 'provide echarts convert http server')
  30. .option('-p, --port <number>', 'change server port when add -s or --server', 9090)
  31. .option('-o, --opt <json>', 'add the param of echarts method [ eChart.setOption(opt) ]')
  32. .option('-t, --type <value>', 'provide file/base64 for image, default file', /^(file|base64)$/i, 'base64')
  33. .option('-f, --outfile <path>', 'add output of the image file path')
  34. .option('-w, --width <number>', 'change image width', '600')
  35. .option('-h, --height <number>', 'change image height', '400')
  36. .parse(system.args);
  37. // ***********************************
  38. // Echarts转换器
  39. // ***********************************
  40. function Convert(params) {
  41. this.params = params || commandParams; // 参数命令
  42. this.external = {
  43. JQUERY3: path + '/script/jquery-3.6.3.min.js',
  44. ECHARTS: path + '/script/echarts.min.js',
  45. ECHARTS_CHINA: path + '/script/china.js'
  46. }; // 外部js
  47. }
  48. /**
  49. * 初始化
  50. */
  51. Convert.prototype.init = function () {
  52. var params = this.params;
  53. this.check(params);
  54. if (params.server) {
  55. this.server(params);
  56. } else {
  57. this.client(params);
  58. }
  59. };
  60. /**
  61. * 参数检查
  62. * @param params
  63. */
  64. Convert.prototype.check = function (params) {
  65. if (undefined === params.server && undefined === params.opt) {
  66. this.error("option argument missing -o, --opt <json>");
  67. }
  68. if (undefined !== params.opt) {
  69. var isJson = this.checkJson(params.opt);
  70. if (!isJson) {
  71. this.error("--opt <json> args not json string");
  72. }
  73. }
  74. if ('file' === params.type && undefined === params.outfile) {
  75. this.createTmpDir();
  76. }
  77. };
  78. /**
  79. * 检查是否是json字符串
  80. * @param value
  81. * @returns {boolean}
  82. */
  83. Convert.prototype.checkJson = function (value) {
  84. var re = /^\{[\s\S]*\}$|^\[[\s\S]*\]$/;
  85. // 类型为string
  86. if (typeof value !== 'string') {
  87. return false;
  88. }
  89. // 正则验证
  90. if (!re.test(value)) {
  91. return false;
  92. }
  93. // 是否能解析
  94. try {
  95. value = "\"" + value + "\"";
  96. JSON.parse(value);
  97. } catch (err) {
  98. return false;
  99. }
  100. return true;
  101. };
  102. /**
  103. * 创建临时目录,并指定输出路径
  104. */
  105. Convert.prototype.createTmpDir = function () {
  106. var fs = require('fs'); // 文件操作
  107. var tmpDir = fs.workingDirectory + '/tmp';
  108. // 临时目录是否存在且可写
  109. if (!fs.exists(tmpDir)) {
  110. if (!fs.makeDirectory(tmpDir)) {
  111. this.error('Cannot make ' + tmpDir + ' directory\n');
  112. }
  113. }
  114. this.params.outfile = tmpDir + "/" + new Date().getTime() + ".png";
  115. };
  116. /**
  117. * 服务
  118. * @param params
  119. */
  120. Convert.prototype.server = function (params) {
  121. var server = require('webserver').create(), // 服务端
  122. convert = this;
  123. var listen = server.listen(params.port, function (request, response) {
  124. /**
  125. * 输出
  126. * @param data
  127. * @param success
  128. */
  129. function write(data, success, msg) {
  130. response.statusCode = 200;
  131. response.headers = {
  132. 'Cache': 'no-cache',
  133. 'Content-Type': 'application/json;charset=utf-8'
  134. };
  135. response.write(convert.serverResult(data, success, msg));
  136. response.close();
  137. }
  138. //获取参数
  139. var args = convert.serverGetArgs(request);
  140. if (args.opt !== undefined) {
  141. var check = convert.serverCheckAndSet(params, args);
  142. if (check) {
  143. convert.client(params, write);
  144. } else {
  145. write("", false, "failed to get image, please check parameter [opt] is a JSON");
  146. }
  147. } else {
  148. write("", false, "failed to get image, missing parameter [opt]");
  149. }
  150. });
  151. // 判断服务是否启动成功
  152. if (!listen) {
  153. this.error("could not create echarts-convert server listening on port " + params.port);
  154. } else {
  155. console.log("echarts-convert server start success. [pid]=" + system.pid);
  156. }
  157. };
  158. /**
  159. * 服务参数检查和赋值
  160. * @param params
  161. * @param args
  162. * @returns {boolean}
  163. */
  164. Convert.prototype.serverCheckAndSet = function (params, args) {
  165. if (this.checkJson(args.opt)) {
  166. params.opt = args.opt;
  167. } else {
  168. return false;
  169. }
  170. if (/^(file|base64)$/i.exec(args.type)) {
  171. params.type = args.type;
  172. }
  173. if (!isNaN(args.width)) {
  174. params.width = args.width;
  175. }
  176. if (!isNaN(args.height)) {
  177. params.height = args.height;
  178. }
  179. return true;
  180. };
  181. /**
  182. * 结果返回
  183. * @param data
  184. * @param success
  185. * @param msg
  186. */
  187. Convert.prototype.serverResult = function (data, success, msg) {
  188. var result = {
  189. code: success ? 1 : 0,
  190. msg: undefined === msg ? success ? "success" : "failure" : msg,
  191. data: data
  192. };
  193. return JSON.stringify(result);
  194. };
  195. /**
  196. * 获取参数
  197. * @param request
  198. * @returns {{}}
  199. */
  200. Convert.prototype.serverGetArgs = function (request) {
  201. var args = {};
  202. if ('GET' === request.method) {
  203. var index = request.url.indexOf('?');
  204. if (index !== -1) {
  205. var getQuery = request.url.substr(index + 1);
  206. args = this.serverParseArgs(getQuery);
  207. }
  208. } else if ('POST' === request.method) {
  209. var postQuery = request.post;
  210. args = this.serverParseArgs(postQuery);
  211. }
  212. return args;
  213. };
  214. /**
  215. * 解析参数
  216. * @param query 字符串
  217. * @returns {{}} 对象
  218. */
  219. Convert.prototype.serverParseArgs = function (query) {
  220. var args = {},
  221. pairs = query.split("&");
  222. for (var i = 0; i < pairs.length; i++) {
  223. var pos = pairs[i].indexOf('=');
  224. if (pos === -1)
  225. continue;
  226. var key = pairs[i].substring(0, pos);
  227. var value = pairs[i].substring(pos + 1);
  228. // 中文解码,必须写两层
  229. value = decodeURIComponent(decodeURIComponent(value));
  230. args[key] = value;
  231. }
  232. return args;
  233. };
  234. /**
  235. * 访问渲染
  236. * @param params
  237. * @param fn
  238. */
  239. Convert.prototype.client = function (params, fn) {
  240. var page = require('webpage').create(); // 客户端
  241. var convert = this,
  242. external = this.external,
  243. render,
  244. output;
  245. /**
  246. * 渲染
  247. * @returns {*}
  248. */
  249. render = function () {
  250. switch (params.type) {
  251. case 'file':
  252. // 渲染图片
  253. page.render(params.outfile);
  254. return params.outfile;
  255. case 'base64':
  256. default:
  257. var base64 = page.renderBase64('PNG');
  258. return base64;
  259. }
  260. };
  261. /**
  262. * 输出
  263. * @param content 内容
  264. * @param success 是否成功
  265. */
  266. output = function (content, success, msg) {
  267. if (params.server) {
  268. fn(content, success, msg);
  269. page.close();
  270. } else {
  271. console.log(success ? "[SUCCESS]:" : "[ERROR]:" + content);
  272. page.close();
  273. convert.exit(params);// exit
  274. }
  275. };
  276. /**
  277. * 页面console监听
  278. * @param msg
  279. * @param lineNum
  280. * @param sourceId
  281. */
  282. page.onConsoleMessage = function (msg, lineNum, sourceId) {
  283. console.log(msg);
  284. };
  285. /**
  286. * 页面错误监听
  287. * @param msg
  288. * @param trace
  289. */
  290. page.onError = function (msg, trace) {
  291. output("", false, msg); // 失败,返回错误信息
  292. };
  293. // 空白页
  294. page.open("about:blank", function (status) {
  295. // 注入依赖js包
  296. var hasJquery = page.injectJs(external.JQUERY3);
  297. var hasEchart = page.injectJs(external.ECHARTS);
  298. var hasEchartChina = page.injectJs(external.ECHARTS_CHINA);
  299. // 检查js是否引用成功
  300. if (!hasJquery && !hasEchart) {
  301. output("Could not found " + external.JQUERY3 + " or " + external.ECHARTS, false);
  302. }
  303. // 创建echarts
  304. page.evaluate(createEchartsDom, params);
  305. // 定义剪切范围,如果定义则截取全屏
  306. page.clipRect = {
  307. top: 0,
  308. left: 0,
  309. width: params.width,
  310. height: params.height
  311. };
  312. // 渲染
  313. var result = render();
  314. // 成功输出,返回图片或其他信息
  315. output(result, true);
  316. });
  317. };
  318. /**
  319. * 创建eCharts Dom层
  320. * @param params 参数
  321. */
  322. function createEchartsDom(params) {
  323. // 动态加载js,获取options数据
  324. $('<script>')
  325. .attr('type', 'text/javascript')
  326. .html('var options = ' + params.opt)
  327. .appendTo(document.head);
  328. // 取消动画,否则生成图片过快,会出现无数据
  329. if (options !== undefined) {
  330. options.animation = false;
  331. }
  332. // body背景设置为白色
  333. $(document.body).css('backgroundColor', 'white');
  334. // echarts容器
  335. var container = $("<div>")
  336. .attr('id', 'container')
  337. .css({
  338. width: params.width,
  339. height: params.height
  340. }).appendTo(document.body);
  341. var eChart = echarts.init(container[0]);
  342. eChart.setOption(options);
  343. }
  344. /**
  345. * debug,将对象转成json对象
  346. * @param obj
  347. */
  348. Convert.prototype.debug = function (obj) {
  349. console.log(JSON.stringify(obj, null, 4));
  350. };
  351. /**
  352. * 错误信息打印并退出
  353. * @param str 错误信息
  354. */
  355. Convert.prototype.error = function (str) {
  356. console.error("Error:" + str);
  357. this.exit();
  358. };
  359. /**
  360. * 退出,参数为空或是server时,不退出
  361. * @param params 参数
  362. */
  363. Convert.prototype.exit = function (params) {
  364. if (undefined === params || undefined === params.server) {
  365. phantom.exit();
  366. }
  367. };
  368. // 构建,入口
  369. new Convert(commandParams).init();
  370. }(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 没有支持的中文, 需要进行安装对应包。也可安装其它字体库,这里不做更多叙述。

  1. 此处执行linux字符集安装即可
  2. 在centos中执行:yum install bitmap-fonts bitmap-fonts-cjk
  3. 在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格式数据生成图表图片

  1. import org.apache.http.HttpEntity;
  2. import org.apache.http.NameValuePair;
  3. import org.apache.http.client.entity.UrlEncodedFormEntity;
  4. import org.apache.http.client.methods.CloseableHttpResponse;
  5. import org.apache.http.client.methods.HttpPost;
  6. import org.apache.http.impl.client.CloseableHttpClient;
  7. import org.apache.http.impl.client.HttpClients;
  8. import org.apache.http.message.BasicNameValuePair;
  9. import org.apache.http.util.EntityUtils;
  10. import java.io.IOException;
  11. import java.util.ArrayList;
  12. import java.util.HashMap;
  13. import java.util.List;
  14. import java.util.Map;
  15. /**
  16. * 以 EchartsData 示例中 getBar() 数据为例,将生成的Echarts数据转为base64图片
  17. *
  18. * @author demain_lee
  19. * @since 2022/12/28
  20. */
  21. public class EchartData2Base64 {
  22. public static void main(String[] args) throws IOException {
  23. // json 格式数据
  24. 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]}]}";
  25. // PhantomJS 服务器地址
  26. String url = "http://localhost:9090";
  27. Map<String, String> map = new HashMap<>();
  28. //此处已将%处理为%25
  29. jsonData = jsonData.replaceAll("\\s+", "").replaceAll("\"", "'").replaceAll("%", "%25");
  30. map.put("opt", jsonData);
  31. String resultData = post(url, map, "utf-8");
  32. System.out.println(resultData);
  33. }
  34. public static String post(String url, Map<String, String> map, String encoding) throws IOException {
  35. String body = "";
  36. // 创建httpclient对象
  37. CloseableHttpClient client = HttpClients.createDefault();
  38. // 创建post方式请求对象
  39. HttpPost httpPost = new HttpPost(url);
  40. // 装填参数
  41. List<NameValuePair> nvp = new ArrayList<>();
  42. if (map != null) {
  43. for (Map.Entry<String, String> entry : map.entrySet()) {
  44. nvp.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
  45. }
  46. }
  47. // 设置参数到请求对象中
  48. httpPost.setEntity(new UrlEncodedFormEntity(nvp, encoding));
  49. // 执行请求操作,并拿到结果(同步阻塞)
  50. CloseableHttpResponse response = client.execute(httpPost);
  51. // 获取结果实体
  52. HttpEntity entity = response.getEntity();
  53. if (entity != null) {
  54. // 按指定编码转换结果实体为String类型
  55. body = EntityUtils.toString(entity, encoding);
  56. }
  57. EntityUtils.consume(entity);
  58. // naps
  59. response.close();
  60. return body;
  61. }
  62. }

3.1 base64格式数据

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

  1. //返回数据
  2. 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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博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/715978
推荐阅读
相关标签
  

闽ICP备14008679号