最近被生成报告纠结了一个礼拜,生成word并不难,可以通过模版来实现,目前用得比较多的技术有,poi,freemarker等等工具,只需要自己写一个模版通过占位符的形式来替换里面的变量。这里生成word就不过多去介绍(后期出一篇文章)但是里面的图表就没有那么好去实现,要考虑到通过后台获取数据来生成图表。经过了一个礼拜的查阅资料找到了两种在后端实现方式,本篇以生成柱状图为例:
一 Jfreechart
- 简介
JFreeChart是JAVA平台上的一个开放的图表绘制类库。它完全使用JAVA语言编写,是为applications, applets, servlets 以及JSP等使用所设计。JFreeChart可生成饼图(pie charts)、柱状图(bar charts)、散点图(scatter plots)、时序图(time series)、甘特图(Gantt charts)等等多种图表,并且可以产生PNG和JPEG格式的输出,还可以与PDF和EXCEL关联。 - 导入依赖
| <dependency> |
| <groupId>jfreechart</groupId> |
| <artifactId>jfreechart</artifactId> |
| <version>1.0.0</version> |
| </dependency> |
复制
- 简单demo
| |
| |
| |
| |
| |
| |
| |
| |
| public static JFreeChart createChart(CategoryDataset categoryDataset) { |
| JFreeChart jfreechart = ChartFactory.createBarChart("Test", "", "", categoryDataset, |
| PlotOrientation.VERTICAL, false, false, false); |
| |
| Font labelFont = new Font(Font.SANS_SERIF, Font.TRUETYPE_FONT, 15); |
| |
| |
| |
| jfreechart.setBackgroundPaint(Color.white); |
| |
| CategoryPlot plot = jfreechart.getCategoryPlot(); |
| |
| |
| plot.setRangeGridlinesVisible(true); |
| |
| plot.setRangeGridlinePaint(Color.gray); |
| |
| NumberAxis vn = (NumberAxis) plot.getRangeAxis(); |
| DecimalFormat df = new DecimalFormat("#0.0"); |
| |
| vn.setNumberFormatOverride(df); |
| |
| |
| CategoryAxis domainAxis = plot.getDomainAxis(); |
| |
| domainAxis.setLabelFont(labelFont); |
| |
| domainAxis.setTickLabelFont(labelFont); |
| |
| domainAxis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(Math.PI / 3.0)); |
| |
| domainAxis.setMaximumCategoryLabelWidthRatio(6.00f); |
| |
| |
| |
| domainAxis.setLowerMargin(0.0); |
| |
| domainAxis.setUpperMargin(0.0); |
| |
| plot.setDomainAxis(domainAxis); |
| |
| plot.setBackgroundPaint(new Color(255, 255, 255, 255)); |
| |
| |
| ValueAxis rangeAxis = plot.getRangeAxis(); |
| rangeAxis.setLabelFont(labelFont); |
| rangeAxis.setTickLabelFont(labelFont); |
| |
| rangeAxis.setUpperMargin(0.15); |
| |
| rangeAxis.setLowerMargin(0.15); |
| plot.setRangeAxis(rangeAxis); |
| |
| |
| TextTitle textTitle = jfreechart.getTitle(); |
| textTitle.setFont(new Font("黑体", Font.PLAIN, 20)); |
| domainAxis.setTickLabelFont(new Font("sans-serif", Font.PLAIN, 15)); |
| domainAxis.setLabelFont(new Font("宋体", Font.PLAIN, 15)); |
| vn.setTickLabelFont(new Font("sans-serif", Font.PLAIN, 15)); |
| vn.setLabelFont(new Font("黑体", Font.PLAIN, 15)); |
| |
| |
| |
| CustomRenderer renderer = new CustomRenderer(); |
| ArrayList<Double> data = new ArrayList<Double>(); |
| data.add(99D); |
| data.add(87D); |
| data.add(89D); |
| data.add(45D); |
| data.add(78D); |
| data.add(92D); |
| data.add(98D); |
| data.add(80D); |
| renderer.setScores(data); |
| |
| renderer.setMaximumBarWidth(0.1); |
| |
| renderer.setMinimumBarLength(0.1); |
| |
| renderer.setBaseOutlinePaint(Color.BLACK); |
| |
| renderer.setDrawBarOutline(true); |
| |
| renderer.setItemMargin(1); |
| jfreechart.getRenderingHints().put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); |
| |
| |
| |
| |
| renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator()); |
| renderer.setBaseItemLabelsVisible(true); |
| plot.setRenderer(renderer); |
| |
| plot.setForegroundAlpha(1.0f); |
| |
| plot.setBackgroundAlpha(0.5f); |
| return jfreechart; |
| } |
复制
| |
| |
| |
| |
| |
| |
| |
| public static CategoryDataset createDataset() { |
| double[][] data = new double[][]{{1,9,0,15,9,3,4}}; |
| String[] rowKeys = {"1"}; |
| String[] columnKeys = {"Test1","Test2","Test3","Test4","Test5","Test6","Test7"}; |
| return DatasetUtilities.createCategoryDataset(rowKeys, columnKeys, data); |
| } |
复制
- 启动测试
| public static void main(String[] args) throws Exception { |
| |
| CategoryDataset dataset = createDataset(); |
| |
| JFreeChart freeChart = createChart(dataset); |
| |
| FileOutputStream out = null; |
| try { |
| out = new FileOutputStream("F:\\chart110605.png"); |
| ChartUtilities.writeChartAsPNG(out, freeChart, |
| 800, 450); |
| System.out.println("图片运行成功"); |
| } catch (FileNotFoundException e) { |
| e.printStackTrace(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } finally { |
| try { |
| out.close(); |
| } catch (final Exception ex) { |
| ex.printStackTrace(); |
| } |
| } |
| |
| } |
复制
- 生成图片展示

第一种方式到此为止,下面上第二种
Echarts插件
- 简介
Echarts在熟悉了解前端的一定不会感到陌生,ECharts是一款基于JavaScript的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。ECharts最初由百度团队开源,并于2018年初捐赠给Apache基金会,成为ASF孵化级项目。
Echarts官网链接: https://echarts.apache.org/zh/index.html
Echarts插件安装包
链接:https://pan.baidu.com/s/19399TOP_2i3GM-wR3Q71XA
提取码:kl66
解压后里面包含phantomjs-2.1.1-windows和echarts-convert.js,进入\echarts\phantomjs-2.1.1-windows\bin目录下通过cmd命令启动phantomjs D:\toos\echarts\saintlee-echartsconvert-master\echartsconvert\echarts-convert.js -s -p 6666
- 导入依赖
| <dependency> |
| <groupId>com.github.abel533</groupId> |
| <artifactId>Echarts</artifactId> |
| <version>3.0.0.6</version> |
| </dependency> |
| |
复制
- 直接上代码
这里是echarts-pojo模块(数据模型)
| |
| |
| |
| |
| |
| |
| |
| @Data |
| public class BarParam { |
| private Object[] barName; |
| |
| private Object[] barValue; |
| |
| private String legendName; |
| } |
复制
| |
| |
| |
| |
| |
| |
| |
| @Data |
| public class BarData { |
| |
| private String title; |
| |
| private BarParam barParamList; |
| |
| private Boolean isHorizontal; |
| |
| |
| |
| } |
| |
复制
对GsonOption.java做一层封装,继承GsonOption 类
| * <h3>demo</h3> |
| * <p></p> |
| * |
| * @author : dkl |
| * @date : 2023-05-12 14:13 |
| **/ |
| @Data |
| public class EnhancedOption extends GsonOption { |
| private String filepath; |
| |
| private static boolean VIEW =false; |
| private static String EXPORT_PATH =""; |
| |
| |
| |
| |
| |
| public void print() { |
| GsonUtil.print(this); |
| } |
| |
| |
| |
| |
| public void printPretty() { |
| GsonUtil.printPretty(this); |
| } |
| |
| |
| |
| |
| public void view() { |
| if (!VIEW) { |
| return; |
| } |
| if (this.filepath != null) { |
| try { |
| OptionUtil.browse(this.filepath); |
| } catch (Exception e) { |
| this.filepath = OptionUtil.browse(this); |
| } |
| } else { |
| this.filepath = OptionUtil.browse(this); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| public String exportToHtml(String fileName) { |
| return exportToHtml(EXPORT_PATH, fileName); |
| } |
| } |
复制
请求工具类
| |
| |
| |
| |
| |
| |
| |
| @Slf4j |
| public class HttpUtil { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static String post(String url, Map<String, String> params, String charset) |
| throws ClientProtocolException, IOException { |
| log.info("httpPostRequest url : " + url + " paramMap : " + params); |
| String responseEntity = ""; |
| |
| |
| CloseableHttpClient client = HttpClients.createDefault(); |
| |
| |
| HttpPost httpPost = new HttpPost(url); |
| |
| |
| List<NameValuePair> nameValuePairs = new ArrayList<>(); |
| if (params != null) { |
| for (Map.Entry<String, String> entry : params.entrySet()) { |
| nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); |
| } |
| } |
| |
| |
| httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, charset)); |
| |
| |
| CloseableHttpResponse response = client.execute(httpPost); |
| |
| |
| HttpEntity entity = response.getEntity(); |
| if (entity != null) { |
| |
| responseEntity = EntityUtils.toString(entity, charset); |
| } |
| |
| |
| EntityUtils.consume(entity); |
| response.close(); |
| |
| |
| return responseEntity; |
| } |
| |
| public static String postUrl(String url, Map<String, Object> params, String charset) { |
| String responseEntity = ""; |
| |
| CloseableHttpClient client = HttpClients.createDefault(); |
| |
| HttpPost httpPost = new HttpPost(url); |
| |
| httpPost.setEntity(new StringEntity(JSONObject.toJSONString(params), charset)); |
| |
| CloseableHttpResponse response = null; |
| try { |
| response = client.execute(httpPost); |
| |
| HttpEntity entity = response.getEntity(); |
| if (entity != null) { |
| |
| responseEntity = EntityUtils.toString(entity, charset); |
| } |
| |
| EntityUtils.consume(entity); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } finally { |
| try { |
| response.close(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| return responseEntity; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static String doPost(String url, String params) throws Exception { |
| |
| log.info("httpPostRequest url : " + url + " paramMap : " + params); |
| |
| CloseableHttpClient httpclient = HttpClients.createDefault(); |
| HttpPost httpPost = new HttpPost(url); |
| httpPost.setHeader("Accept", "application/json"); |
| httpPost.setHeader("Content-Type", "application/json"); |
| String charSet = "UTF-8"; |
| StringEntity entity = new StringEntity(params, charSet); |
| |
| httpPost.setEntity(entity); |
| CloseableHttpResponse response = null; |
| |
| try { |
| |
| response = httpclient.execute(httpPost); |
| |
| StatusLine status = response.getStatusLine(); |
| int state = status.getStatusCode(); |
| if (state == HttpStatus.SC_OK) { |
| HttpEntity responseEntity = response.getEntity(); |
| String jsonString = EntityUtils.toString(responseEntity); |
| log.info("post请求响应结果:{}", jsonString); |
| return jsonString; |
| } |
| else{ |
| log.error("请求返回:"+state+"("+url+")"); |
| } |
| } |
| finally { |
| if (response != null) { |
| try { |
| response.close(); |
| } catch (IOException e) { |
| log.error(e.getMessage()); |
| } |
| } |
| try { |
| httpclient.close(); |
| } catch (IOException e) { |
| log.error(e.getMessage()); |
| } |
| } |
| return null; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static String doPostJson(String url, JSONObject paramsJson) { |
| log.info("httpPostRequest url : " + url + " paramMap : " + paramsJson); |
| if(StringUtils.isBlank(url)){ |
| log.error("httpPostRequest url is null"); |
| return null; |
| } |
| String result = ""; |
| try { |
| |
| CloseableHttpClient httpClient = HttpClients.createDefault(); |
| |
| HttpPost httpPost = new HttpPost(url); |
| |
| RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(10000) |
| .setConnectionRequestTimeout(10000) |
| .setSocketTimeout(30000) |
| .build(); |
| |
| httpPost.setConfig(requestConfig); |
| |
| httpPost.addHeader("content-type", "application/json;charset=utf-8"); |
| |
| httpPost.setEntity(new StringEntity(paramsJson.toJSONString(), Charset.forName("UTF-8"))); |
| |
| |
| CloseableHttpResponse httpResponse = httpClient.execute(httpPost); |
| |
| result = EntityUtils.toString(httpResponse.getEntity()); |
| |
| } catch (UnsupportedEncodingException e) { |
| log.error("URLUtil.httpPostRequest encounters an UnsupportedEncodingException : {}",e); |
| } catch (IOException e) { |
| log.error("URLUtil.httpPostRequest encounters an IOException : {}",e); |
| } |
| log.info("URLUtil.httpPostRequest -----result----: " + result); |
| return result; |
| } |
| } |
| **文件工具类** |
复制
| |
| |
| |
| |
| |
| |
| |
| |
| public class FileUtil { |
| public static File generateImage(String base64, String path) throws IOException { |
| BASE64Decoder decoder = new BASE64Decoder(); |
| File file = new File(path); |
| String fileName = file.getName(); |
| System.out.println("file = " + file); |
| |
| |
| |
| |
| 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(); |
| return file; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } |
| |
| public static void deleteFile(File file) { |
| |
| String fileName = file.getName(); |
| |
| if (file.exists() && file.isFile()) { |
| if (file.delete()) { |
| System.out.println("删除单个文件" + fileName + "成功!"); |
| } else { |
| System.out.println("删除单个文件" + fileName + "失败!"); |
| } |
| } else { |
| System.out.println("删除单个文件失败:" + fileName + "不存在!"); |
| } |
| } |
| |
| } |
复制
EchartsUtil.java:将option转化为图片编码base64
| |
| |
| |
| |
| |
| |
| |
| public class EchartsUtil { |
| |
| |
| private static final String SUCCESS_CODE = "1"; |
| |
| public static String generateEchartsBase64(String option, String url) throws ClientProtocolException, IOException { |
| String base64 = ""; |
| if (option == null) { |
| return base64; |
| } |
| option = option.replaceAll("\\s+", "").replaceAll("\"", "'"); |
| |
| |
| Map<String, String> params = new HashMap<>(); |
| params.put("opt", option); |
| String response = HttpUtil.post(url, params, "utf-8"); |
| |
| |
| JSONObject responseJson = JSONObject.parseObject(response); |
| String code = responseJson.getString("code"); |
| |
| |
| if (SUCCESS_CODE.equals(code)) { |
| base64 = responseJson.getString("data"); |
| } |
| |
| else { |
| String string = responseJson.getString("msg"); |
| throw new RuntimeException(string); |
| } |
| |
| return base64; |
| } |
| } |
复制
生成柱状图
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static GsonOption createBar(BarData barData){ |
| String title = barData.getTitle(); |
| boolean isHorizontal = barData.getIsHorizontal(); |
| Object[] xdatas = barData.getBarParamList().getBarName(); |
| Object[] ydatas = barData.getBarParamList().getBarValue(); |
| String legendName = barData.getBarParamList().getLegendName(); |
| |
| Bar bar = new Bar(); |
| |
| EnhancedOption option = new EnhancedOption(); |
| option.title(title); |
| option.title().textStyle().fontSize(15).color("#000000").fontWeight("bolder"); |
| |
| |
| option.toolbox().show(true).feature(Tool.mark, |
| Tool.dataView, |
| new MagicType(Magic.line, Magic.bar), |
| Tool.restore, |
| Tool.saveAsImage |
| ); |
| option.toolbox().show(true).feature(); |
| |
| |
| option.tooltip().show(true).formatter("{a}<br/>{b} : {c}"); |
| |
| |
| Legend legend = new Legend(); |
| TextStyle textStyle = new TextStyle(); |
| textStyle.color("red"); |
| textStyle.fontSize(10); |
| textStyle.fontWeight("bolder"); |
| legend.setData(Collections.singletonList(legendName)); |
| legend.setTextStyle(textStyle); |
| |
| option.setLegend(legend); |
| |
| |
| AxisLabel axisLabel = new AxisLabel(); |
| TextStyle textStyle1 = new TextStyle(); |
| textStyle1.fontSize(10); |
| textStyle1.fontWeight("bolder"); |
| axisLabel.show(true); |
| axisLabel.textStyle(textStyle1); |
| axisLabel.setRotate(40); |
| axisLabel.setInterval(0); |
| |
| |
| AxisLine axisLine = new AxisLine(); |
| LineStyle lineStyle = new LineStyle(); |
| lineStyle.color("#000000"); |
| lineStyle.width(4); |
| axisLine.lineStyle(lineStyle); |
| |
| |
| |
| CategoryAxis category = new CategoryAxis(); |
| category.data(xdatas); |
| category.axisLabel(axisLabel); |
| category.axisLine(axisLine); |
| |
| |
| |
| |
| ValueAxis valueAxis = new ValueAxis(); |
| valueAxis.setName("次数"); |
| valueAxis.axisLabel().show(true).textStyle().fontSize(15).fontWeight("bolder"); |
| valueAxis.axisLine().lineStyle().color("#315070").width(4); |
| |
| |
| bar.name(legendName); |
| |
| Normal normal = new Normal(); |
| normal.setShow(true); |
| if(barData.getIsHorizontal() == false){ |
| normal.position(Position.inside); |
| }else { |
| normal.position(Position.top); |
| } |
| normal.color("green"); |
| normal.textStyle().color("red").fontSize(15).fontWeight("bolder"); |
| bar.setBarWidth("70"); |
| |
| |
| bar.label().normal(normal); |
| |
| |
| for(int i = 0;i < xdatas.length;i++){ |
| int data = (int) ydatas[i]; |
| String color = "rgb(251,153,2)"; |
| |
| Map<String, Object> map = new HashMap<>(2); |
| map.put("value", data); |
| |
| map.put("itemStyle", new ItemStyle().normal(new Normal().color(color))); |
| |
| bar.data(map); |
| } |
| if(isHorizontal){ |
| option.xAxis(category); |
| option.yAxis(valueAxis); |
| }else { |
| option.xAxis(valueAxis); |
| option.yAxis(category); |
| } |
| |
| option.series(bar); |
| |
| return option; |
| } |
复制
- 启动测试
| |
| |
| |
| |
| |
| |
| @Slf4j |
| public class EchartBar { |
| private static String requestUrl= "http://127.0.0.1:6666"; |
| private static String imgUrl= "F:/"; |
| public static void main(String[] args) { |
| BarData barData = new BarData(); |
| barData.setTitle("Test"); |
| barData.setIsHorizontal(true); |
| BarParam barParam = new BarParam(); |
| String[] columnKeys = {"Test1","Test2","Test3","Test4","Test5","Test6","Test7"}; |
| barParam.setBarName(columnKeys); |
| Object[] data ={1,9,0,15,9,3,4}; |
| barParam.setBarValue(data); |
| barData.setBarParamList(barParam); |
| |
| GsonOption option = createBar(barData); |
| String optionStr = JSONObject.toJSONString(option); |
| log.error(optionStr); |
| |
| if(optionStr == null || "".equals(optionStr)){ |
| return; |
| } |
| try { |
| String base64 = EchartsUtil.generateEchartsBase64(optionStr, requestUrl); |
| log.info("base64:" + base64); |
| long nowStr = Calendar.getInstance().getTimeInMillis(); |
| |
| String imageName = JDate.getDuanshijian(new JDate(),"yyyy-MM-dd_HH_mm_ss") +".png"; |
| log.info("bar图片:" + imageName); |
| File oldfile = FileUtil.generateImage(base64, imgUrl+imageName); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
复制
- 生成图片展示

两种方案到这就都实现了,来讲一下这两种的优缺点吧
- JFreeChart 方便快捷,不需要自己去安装启动什么服务,而Echarts插件需要安装启动一个服务
- JFreeChart 生成的图片清晰度感觉不是很清晰,Echarts的X轴字符太多导致展示不齐全
以上是个人觉得,不诋毁。可能是我没有细去研究,有知道解决方案的可以指导一下,上面有什么地方不懂的可以评论,看到必回的,让兄弟们不要在里面碰壁。