在实际的生产应用中,我们常常需要将数据进行可视化,生成一些图文报表以供前端使用与查看。而我们使用的最多的图表生成插件工具就是Echarts。为了生成相关的图文报表,我们可以通过前端结合js等来生成,另外也可以使用纯后台(Java代码)来生成。这里我们就介绍使用SpringBoot框架通过API传递参数的方式,纯Java代码而不使用前端来生成相关的图表。
本篇以生成柱状图为例:
一、项目的工程结构
二、项目依赖说明
(1)pom.xml依赖配置如下:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.9</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.59</version> </dependency> <!-- https://mvnrepository.com/artifact/com.github.abel533/ECharts --> <dependency> <groupId>com.github.abel533</groupId> <artifactId>ECharts</artifactId> <version>3.0.0.6</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.7</version> </dependency> <!-- 添加swagger2 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <!-- swagger2-UI --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
(2)application.properties属性文件配置
server.port=8095 img-url=image/ request-url=http://127.0.0.1:6666 img-url-path=F:/echarts/ #HttpServletRequest 的属性是否可以覆盖 controller 中 model 的同名项 spring.freemarker.allow-request-override=false #HttpSession 的属性是否可以覆盖 controller 中 model 的同名项 spring.freemarker.allow-session-override=false #是否开启缓存 spring.freemarker.cache=false #模板文件编码 spring.freemarker.charset=UTF-8 #是否检查模板位置 spring.freemarker.check-template-location=true #Content-Type 的值 spring.freemarker.content-type=text/html #是否将 HttpServletRequest 中的属性添加到 Model 中 spring.freemarker.expose-request-attributes=false #是否将 HttpSession 中的属性添加到 Model 中 spring.freemarker.expose-session-attributes=false #模板文件后缀 spring.freemarker.suffix=.ftl #模板文件位置 spring.freemarker.template-loader-path=classpath: /templates/
三、项目代码说明
(1)common模块-JsonResult.java
package com.lhf.springboot.common; /** * @ClassName: JsonResult * @Author: liuhefei * @Description: TODD * @Date: 2019/8/13 17:55 */ public class JsonResult<T> { private int status = 0; private T data; private String errMsg; public JsonResult(T data) { this.data = data; } public JsonResult(int status, String errMsg) { this.status = status; this.errMsg = errMsg; } public JsonResult(int status, T data, String errMsg) { this.status = status; this.data = data; this.errMsg = errMsg; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getErrMsg() { return errMsg; } public void setErrMsg(String errMsg) { this.errMsg = errMsg; } }
(2)config模块-SwaggerConfig.java
package com.lhf.springboot.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** * @ClassName: SwaggerConfig * @Author: liuhefei * @Description: TODD * @Date: 2019/5/29 19:26 */ @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.lhf.springboot")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder().title("Api接口接口") .description("api接口描述信息") .contact(new Contact("liuhefei", "https://www.imooc.com/u/1323320", "2510736432@qq.com")) .termsOfServiceUrl("https://swagger.io/swagger-ui/") .version("1.0") .build(); } }
(3)echarts-pojo模块(数据模型)-BarData.java
package com.lhf.springboot.echarts.pojo; import lombok.Data; /** * @ClassName: BarData * @Author: liuhefei * @Description: TODD * @Date: 2019/8/15 16:08 */ public class BarData { private String title; //标题 private BarParam barParamList; private Boolean isHorizontal; //是否水平放置 //省略get/set方法 }
(4)echarts-pojo模块(数据模型)-BarParam.java
package com.lhf.springboot.echarts.pojo; /** * @ClassName: BarParam * @Author: liuhefei * @Description: TODD * @Date: 2019/8/15 16:11 */ public class BarParam { private Object[] barName; private Object[] barValue; private String legendName; //省略get/set方法 }
(5)echarts模块-EchartsConfig.java(接口)
package com.lhf.springboot.echarts; /** * @ClassName: EchartsConfig * @Author: liuhefei * @Description: TODD * @Date: 2019/8/22 18:16 */ public interface EchartsConfig { /** * 测试文件生成的目录 */ String EXPORT_PATH = ""; /** * 通过view控制所有测试是否打开浏览器 */ Boolean VIEW = true; }
(6)echarts模块-EnhancedOption.java(实现类,对GsonOption.java做一层封装)
package com.lhf.springboot.echarts; import com.github.abel533.echarts.json.GsonOption; import com.github.abel533.echarts.json.GsonUtil; import com.github.abel533.echarts.json.OptionUtil; /** * @ClassName: EnhancedOption * @Author: liuhefei * @Description: TODD * @Date: 2019/8/22 18:15 */ public class EnhancedOption extends GsonOption implements EchartsConfig { private String filepath; /** * 输出到控制台 */ 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); } } /** * 导出到指定文件名 * * @param fileName * @return 返回html路径 */ public String exportToHtml(String fileName) { return exportToHtml(EXPORT_PATH, fileName); } }
(7)echarts-option模块(组装图表option)-EchartBar.java(组装柱状图option)(核心)
package com.lhf.springboot.echarts.option; import com.github.abel533.echarts.Legend; import com.github.abel533.echarts.axis.AxisLabel; import com.github.abel533.echarts.axis.AxisLine; import com.github.abel533.echarts.axis.CategoryAxis; import com.github.abel533.echarts.axis.ValueAxis; import com.github.abel533.echarts.code.Magic; import com.github.abel533.echarts.code.Position; import com.github.abel533.echarts.code.Tool; import com.github.abel533.echarts.feature.MagicType; import com.github.abel533.echarts.json.GsonOption; import com.github.abel533.echarts.series.Bar; import com.github.abel533.echarts.series.Series; import com.github.abel533.echarts.style.ItemStyle; import com.github.abel533.echarts.style.LineStyle; import com.github.abel533.echarts.style.TextStyle; import com.github.abel533.echarts.style.itemstyle.Normal; import com.lhf.springboot.echarts.EnhancedOption; import com.lhf.springboot.echarts.pojo.BarData; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * @ClassName: EchartBar * @Author: liuhefei * @Description: TODD * @Date: 2019/8/15 15:26 */ public class EchartBar { /** * 生成单柱状图 * @param //isHorizontal 是否水平放置 * @param //color 柱状图颜色,可以不设置,默认为红色 * @param //title 柱状图标题 * @param //xdatas 横轴数据 * @param //ydatas 纵轴数据 * @return */ public static GsonOption createBar(BarData barData){ /*String[] citis = { "广州", "深圳", "珠海", "汕头", "韶关", "佛山" }; int[] datas = { 6030, 7800, 5200, 3444, 2666, 5708 }; String title = "地市数据"; String[] colors = { "rgb(2,111,230)", "rgb(186,73,46)", "rgb(78,154,97)", "rgb(2,111,230)", "rgb(186,73,46)", "rgb(78,154,97)" };*/ String title = barData.getTitle(); boolean isHorizontal = barData.getHorizontal(); Object[] xdatas = barData.getBarParamList().getBarName(); Object[] ydatas = barData.getBarParamList().getBarValue(); String legendName = barData.getBarParamList().getLegendName(); Bar bar = new Bar(); //图类别(柱状图) //title EnhancedOption option = new EnhancedOption(); option.title(title); //标题 option.title().textStyle().fontSize(15).color("red").fontWeight("bolder"); //工具栏 toolbox /*option.toolbox().show(true).feature(Tool.mark, //辅助线 Tool.dataView, //数据视图 new MagicType(Magic.line, Magic.bar), //线图,柱状图切换 Tool.restore, //还原 Tool.saveAsImage //保存图片 ); option.toolbox().show(true).feature();*/ //tooltip option.tooltip().show(true).formatter("{a}<br/>{b} : {c}"); //显示工具提示,设置提示格式 //legend Legend legend = new Legend(); TextStyle textStyle = new TextStyle(); textStyle.color("red"); textStyle.fontSize(15); textStyle.fontWeight("bolder"); legend.setData(Collections.singletonList(legendName)); legend.setTextStyle(textStyle); option.setLegend(legend); //图例 //axisLabel AxisLabel axisLabel = new AxisLabel(); TextStyle textStyle1 = new TextStyle(); textStyle1.fontSize(15); textStyle1.fontWeight("bolder"); axisLabel.show(true); axisLabel.textStyle(textStyle1); //axisLine AxisLine axisLine = new AxisLine(); LineStyle lineStyle = new LineStyle(); lineStyle.color("#315070"); lineStyle.width(4); axisLine.lineStyle(lineStyle); //xAxis CategoryAxis category = new CategoryAxis();// 轴分类 category.data(xdatas);// 轴数据类别 category.axisLabel(axisLabel); // x轴文字样式 category.axisLine(axisLine); //x轴样式 //yAxis ValueAxis valueAxis = new ValueAxis(); valueAxis.axisLabel().show(true).textStyle().fontSize(15).fontWeight("bolder"); //y轴文字样式 valueAxis.axisLine().lineStyle().color("#315070").width(4); //y轴样式 //series bar.name(legendName); Normal normal = new Normal(); normal.setShow(true); if(barData.getHorizontal() == false){ normal.position(Position.inside); }else { normal.position(Position.top); } normal.color("green"); normal.textStyle().color("red").fontSize(15).fontWeight("bolder"); //bar.setBarWidth("40"); //柱条宽度 //bar.setBarMaxWidth(100); //柱条最大宽度 //bar.setBarMinHeight(10); //柱条最小高度 bar.label().normal(normal); //循环数据 for(int i = 0;i < xdatas.length;i++){ int data = (int) ydatas[i]; String color = "rgb(2,111,230)"; //类目对应的柱状图 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); //x轴 option.yAxis(valueAxis); //y轴 }else { //横轴为值,纵轴为类别 option.xAxis(valueAxis); //x轴 option.yAxis(category); //y轴 } option.series(bar); return option; } }
(8)util模块工具类:这些工具类都是比较常用的,也可以用到其他地方,因此这里我都列举出来
EchartsUtil.java:将option转化为图片编码base64
package com.lhf.springboot.util; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.apache.http.client.ClientProtocolException; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * @ClassName: EchartsUtil * @Author: liuhefei * @Description: 生成echarts工具类:将生成的option转化为base64编码 * @Date: 2019/8/15 12:10 */ public class EchartsUtil { //private static String url = "http://localhost:6666"; 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("\"", "'"); // 将option字符串作为参数发送给echartsConvert服务器 Map<String, String> params = new HashMap<>(); params.put("opt", option); String response = HttpUtil.post(url, params, "utf-8"); // 解析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; } }
FileUtil.java: 将图片base64编码转化为图片文件
package com.lhf.springboot.util; import sun.misc.BASE64Decoder; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; /** * @ClassName: FileUtil * @Author: liuhefei * @Description: 将图片base64编码转化为图片 * @Date: 2019/8/15 18:52 */ 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); //创建临时文件 //File tempFile = File.createTempFile(fileName, ".png"); //FileOutputStream fos = new FileOutputStream(tempFile);*/ 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; } /* finally { //关闭临时文件 fos.flush(); fos.close(); try { Thread.sleep(10000); tempFile.deleteOnExit();//程序退出时删除临时文件 } catch (InterruptedException e) { e.printStackTrace(); } } */ } public static void deleteFile(File file) { //File file = new 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 + "不存在!"); } } }
HttpUtil.java:Http请求工具类
package com.lhf.springboot.util; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.apache.commons.lang3.StringUtils; import org.apache.http.*; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; 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.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * @ClassName: HttpUtil * @Author: liuhefei * @Description: Http工具类 * @Date: 2019/8/15 12:10 */ public class HttpUtil { private static Logger logger = LoggerFactory.getLogger(HttpUtil.class); /** * 发送post请求 * @param url * @param params * @param charset * @return * @throws ClientProtocolException * @throws IOException */ public static String post(String url, Map<String, String> params, String charset) throws ClientProtocolException, IOException { logger.info("httpPostRequest url : " + url + " paramMap : " + params); String responseEntity = ""; // 创建CloseableHttpClient对象 CloseableHttpClient client = HttpClients.createDefault(); // 创建post方式请求对象 HttpPost httpPost = new HttpPost(url); // 生成请求参数 List<NameValuePair> nameValuePairs = new ArrayList<>(); if (params != null) { for (Entry<String, String> entry : params.entrySet()) { nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } } // 将参数添加到post请求中 httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, charset)); // 发送请求,获取结果(同步阻塞) CloseableHttpResponse response = client.execute(httpPost); // 获取响应实体 HttpEntity entity = response.getEntity(); if (entity != null) { // 按指定编码转换结果实体为String类型 responseEntity = EntityUtils.toString(entity, charset); } // 释放资源 EntityUtils.consume(entity); response.close(); //System.out.println("responseEntity = " + responseEntity); return responseEntity; } public static String postUrl(String url, Map<String, Object> params, String charset) { String responseEntity = ""; // 创建CloseableHttpClient对象 CloseableHttpClient client = HttpClients.createDefault(); // 创建post方式请求对象 HttpPost httpPost = new HttpPost(url); // 将参数添加到post请求中 httpPost.setEntity(new StringEntity(JSON.toJSONString(params), charset)); // 发送请求,获取结果(同步阻塞) CloseableHttpResponse response = null; try { response = client.execute(httpPost); // 获取响应实体 HttpEntity entity = response.getEntity(); if (entity != null) { // 按指定编码转换结果实体为String类型 responseEntity = EntityUtils.toString(entity, charset); } // 释放资源 EntityUtils.consume(entity); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { e.printStackTrace(); } } return responseEntity; } /** * post请求(用于请求json格式的参数) * @param url * @param params * @return */ public static String doPost(String url, String params) throws Exception { logger.info("httpPostRequest url : " + url + " paramMap : " + params); CloseableHttpClient httpclient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(url);// 创建httpPost httpPost.setHeader("Accept", "application/json"); httpPost.setHeader("Content-Type", "application/json"); String charSet = "UTF-8"; StringEntity entity = new StringEntity(params, charSet); //logger.info("entity = " + entity); httpPost.setEntity(entity); CloseableHttpResponse response = null; try { response = httpclient.execute(httpPost); //logger.info("response = " + response); StatusLine status = response.getStatusLine(); int state = status.getStatusCode(); if (state == HttpStatus.SC_OK) { HttpEntity responseEntity = response.getEntity(); String jsonString = EntityUtils.toString(responseEntity); logger.info("post请求响应结果:{}", jsonString); return jsonString; } else{ logger.error("请求返回:"+state+"("+url+")"); } } finally { if (response != null) { try { response.close(); } catch (IOException e) { logger.error(e.getMessage()); } } try { httpclient.close(); } catch (IOException e) { logger.error(e.getMessage()); } } return null; } /** * http发送POST请求 * * @author J.M.C * @since 2019年1月16日 * @param url 长连接URL * @param paramsJson 请求参数body * @return result 字符串 */ public static String doPostJson(String url, JSONObject paramsJson) { logger.info("httpPostRequest url : " + url + " paramMap : " + paramsJson); if(StringUtils.isBlank(url)){ logger.error("httpPostRequest url is null"); return null; } String result = ""; try { // 创建httpClient实例 CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建httpPost远程连接实例 HttpPost httpPost = new HttpPost(url); // 配置请求参数实例 RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(10000)// 设置连接主机服务超时时间 .setConnectionRequestTimeout(10000)// 设置连接请求超时时间 .setSocketTimeout(30000)// 设置读取数据连接超时时间 .build(); // 为httpPost实例设置配置 httpPost.setConfig(requestConfig); // 设置请求头 httpPost.addHeader("content-type", "application/json;charset=utf-8"); // 封装post请求参数 httpPost.setEntity(new StringEntity(paramsJson.toJSONString(), Charset.forName("UTF-8"))); // httpClient对象执行post请求,并返回响应参数对象 // HttpResponse httpResponse = httpClient.execute(httpPost); CloseableHttpResponse httpResponse = httpClient.execute(httpPost); // 从响应对象中获取响应内容 result = EntityUtils.toString(httpResponse.getEntity()); //logger.info("result = {}" , result); } catch (UnsupportedEncodingException e) { logger.error("URLUtil.httpPostRequest encounters an UnsupportedEncodingException : {}",e); } catch (IOException e) { logger.error("URLUtil.httpPostRequest encounters an IOException : {}",e); } logger.info("URLUtil.httpPostRequest -----result----: " + result); return result; } public static String send(String url, JSONObject jsonObject,String encoding) throws Exception{ logger.info("httpPostRequest url : " + url + " jsonObject : " + jsonObject); String body = ""; //创建httpclient对象 CloseableHttpClient client = HttpClients.createDefault(); //创建post方式请求对象 HttpPost httpPost = new HttpPost(url); String strParam = JSONObject.toJSONString(jsonObject); //System.out.println("strParam = " + strParam); //装填参数 StringEntity entity = new StringEntity(strParam, "utf-8"); entity.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json")); //设置参数到请求对象中 httpPost.setEntity(entity); //System.out.println("请求地址:"+ url); //System.out.println("请求参数:"+ entity.toString()); //设置header信息 //指定报文头【Content-type】、【User-Agent】 //httpPost.setHeader("Content-type", "application/x-www-form-urlencoded"); httpPost.setHeader("Content-type", "application/json"); httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"); //执行请求操作,并拿到结果(同步阻塞) CloseableHttpResponse response = client.execute(httpPost); //获取结果实体 HttpEntity entityResult = response.getEntity(); if (entityResult != null) { //按指定编码转换结果实体为String类型 body = EntityUtils.toString(entityResult, encoding); } EntityUtils.consume(entityResult); //释放链接 response.close(); //logger.info("body = {}", body); return body; } public static JSONObject doPost(String url,JSONObject json){ logger.info("httpPostRequest url : " + url + " jsonObject : " + json); CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost post = new HttpPost(url); JSONObject response = null; try { StringEntity s = new StringEntity(json.toString()); s.setContentEncoding("UTF-8"); s.setContentType("application/json");//发送json数据需要设置contentType post.setEntity(s); HttpResponse res = httpClient.execute(post); if(res.getStatusLine().getStatusCode() == HttpStatus.SC_OK){ HttpEntity entity = res.getEntity(); String result = EntityUtils.toString(res.getEntity());// 返回json格式: //logger.info("result = {}", result); response = (JSONObject) JSONObject.parse(result); } } catch (Exception e) { throw new RuntimeException(e); } //logger.info("response = {}", response); return response; } }
ImageUtil.java:给图片添加水印
package com.lhf.springboot.util; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; /** * @ClassName: ImageUtil * @Author: liuhefei * @Description: 给图片添加水印工具类 * @Date: 2019/9/10 12:32 */ public class ImageUtil { /** * 给图片添加水印文字、可设置水印文字的旋转角度 * @param logoText 要写入的文字 * @param srcImgPath 源图片路径 * @param newImagePath 新图片路径 * @param degree 旋转角度 * @param color 字体颜色 * @param formaName 图片后缀 */ public static void markImageByText(String logoText, String srcImgPath, String newImagePath, Integer degree, Color color, String formaName) { InputStream is = null; OutputStream os = null; try { // 1、源图片 Image srcImg = ImageIO.read(new File(srcImgPath)); BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null),srcImg.getHeight(null), BufferedImage.TYPE_INT_RGB); // 2、得到画笔对象 Graphics2D g = buffImg.createGraphics(); // 3、设置对线段的锯齿状边缘处理 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(srcImg.getScaledInstance(srcImg.getWidth(null), srcImg.getHeight(null), Image.SCALE_SMOOTH), 0, 0, null); // 4、设置水印旋转 if (null != degree) { g.rotate(Math.toRadians(degree), buffImg.getWidth()/2,buffImg.getHeight() /2); } // 5、设置水印文字颜色 g.setColor(color); // 6、设置水印文字Font g.setFont(new Font("宋体", Font.BOLD, buffImg.getHeight() /6)); // 7、设置水印文字透明度 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.15f)); // 8、第一参数->设置的内容,后面两个参数->文字在图片上的坐标位置(x,y) g.drawString(logoText, buffImg.getWidth()/4 , buffImg.getHeight()/2); // 9、释放资源 g.dispose(); // 10、生成图片 os = new FileOutputStream(newImagePath); ImageIO.write(buffImg, formaName, os); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != is) is.close(); } catch (Exception e) { e.printStackTrace(); } try { if (null != os) os.close(); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { markImageByText("霜花似雪","F:\\echarts\\bar1567600917165oNeL.png","F:\\echarts\\new\\bar1567600917135oNeL.png",45,new Color(200,10,1),"png"); } }
RandomUtils.java
package com.lhf.springboot.util; import java.util.Random; /** * @ClassName: RandomUtils * @Desc: 生成随机字符串 * @Author: liuhefei * @Date: 2019/2/28 14:16 */ public class RandomUtils { public static String getRandomString(int length){ //1. 定义一个字符串(A-Z,a-z,0-9)即62个数字字母; String str="zxcvbnmlkjhgfdsaqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890"; //2. 由Random生成随机数 Random random=new Random(); StringBuffer sb=new StringBuffer(); //3. 长度为几就循环几次 for(int i=0; i<length; ++i){ //从62个的数字或字母中选择 int number=random.nextInt(62); //将产生的数字通过length次承载到sb中 sb.append(str.charAt(number)); } //将承载的字符转换成字符串 return sb.toString(); } public static String getRandomNum(int length){ //1. 定义一个字符串(A-Z,a-z,0-9)即62个数字字母; String str="1234567890"; //2. 由Random生成随机数 Random random=new Random(); StringBuffer sb=new StringBuffer(); //3. 长度为几就循环几次 for(int i=0; i<length; ++i){ //从10个的数字中选择 int number=random.nextInt(10); //将产生的数字通过length次承载到sb中 sb.append(str.charAt(number)); } //将承载的字符转换成字符串 return sb.toString(); } public static void main(String[] args) { //这里的32是生成32位随机码,根据你的需求,自定义 String random1 = getRandomString(32); System.out.println(random1); String random2 = getRandomNum(32); System.out.println(random2); } }
(8)controller模块(API接口)- EchartsController.java
package com.lhf.springboot.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.github.abel533.echarts.json.GsonOption; import com.lhf.springboot.common.JsonResult; import com.lhf.springboot.echarts.option.EchartBar; import com.lhf.springboot.echarts.option.EchartLine; import com.lhf.springboot.echarts.option.EchartPie; import com.lhf.springboot.echarts.pojo.BarData; import com.lhf.springboot.echarts.pojo.LinesData; import com.lhf.springboot.echarts.pojo.PieData; import com.lhf.springboot.util.*; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.jboss.logging.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.awt.*; import java.io.File; import java.util.Calendar; import java.util.HashMap; import java.util.Map; /** * @ClassName: EchartsController * @Author: liuhefei * @Description: 利用Java代码生成柱状图和折线图 * @Date: 2019/9/26 10:37 */ @Api(value = "生成Echarts图表API接口", tags = "Echarts图表") @RequestMapping("/echarts") @RestController public class EchartsController { private final static Logger logger = Logger.getLogger(EchartsController.class); @Value("${img-url}") private String imgUrl; @Value("${img-url-path}") private String imgUrlPath; @Value("${request-url}") private String requestUrl; @ApiOperation(value = "生成柱状图") @RequestMapping(value = "/bar", method = RequestMethod.POST) public JsonResult createBar(@RequestBody BarData barData){ GsonOption option = EchartBar.createBar(barData); String optionStr = JSONObject.toJSONString(option); if(optionStr == null || "".equals(optionStr)){ return new JsonResult(-1, "Fail"); } logger.info("bar-optionStr = " + optionStr); File oldfile = null; File newfile = null; String oldFilePath = null; String newFilePath = null; try { // 根据option参数发起请求,转换为base64 String base64 = EchartsUtil.generateEchartsBase64(optionStr, requestUrl); long nowStr = Calendar.getInstance().getTimeInMillis(); //图片名 String imageName = "bar"+nowStr+ RandomUtils.getRandomString(4)+".png"; logger.info("bar图片:" + imageName); oldfile = FileUtil.generateImage(base64, imgUrl+imageName); newfile = new File(imgUrl+"new"+imageName); oldFilePath = imgUrl+imageName; newFilePath = imgUrl+"new"+imageName; logger.info("file = " + oldfile); logger.info("oldFilePath = " + oldFilePath); logger.info("newFilePath = " + newFilePath); String logoText = "霜花似雪"; //添加水印 ImageUtil.markImageByText(logoText, oldFilePath, newFilePath, -15, new Color(190,190,190), "png"); }catch (Exception e){ logger.error("发生了异常," + e.getMessage()); return new JsonResult(-1, "Fail"); } return new JsonResult(1, newFilePath, "SUCCESS"); } }
四、插件环境配置
为了生成图片,我们需要借助插件来实现,这里我们以windows环境为主来说明
需要的插件:phantomjs-2.1.1-windows.zip 和 saintlee-echartsconvert-master.zip
(1)phantomjs-2.1.1-windows.zip下载地址:
https://phantomjs.org/download.html
API地址:http://phantomjs.org/api/
(2)saintlee-echartsconvert-master.zip下载地址:(github地址及文档)https://gitee.com/saintlee/echartsconvert/ 说明:此插件有缺陷,生成不了饼图,这个后面再说。
安装配置:
1. 首先需要安装这两个插件
2. 可将phantomjs-2.1.1-windows.zip添加到系统path变量下,我的安装路径:D:\softpack\echarts\phantomjs-2.1.1-windows\bin(此变量加到path变量环境下),也可以不操作此步
3. 配置好后,启动服务,启动cmd命令窗口,命令行输入<phantomjs路径> <EChartsConvert路径> -s -p <服务端口号>
五、结合Swagger文档测试
环境配置完成之后,启动服务,swagger文档:http://localhost:8095/swagger-ui.html
测试数据:
{ "barParamList": { "barName": [ "A罩杯", "B罩杯", "C罩杯", "D罩杯", "E罩杯", "F罩杯","G罩杯" ], "barValue": [ 43364, 13899, 12000, 2181, 21798, 1796, 1300 ], "legendName": "胸罩图例" }, "horizontal": true, "title": "胸罩使用人数" }
效果展示:
由于篇幅太长,分享就到这里,未完待续!
另外推荐为大家推荐几门与Java相关的实战课程,望笑纳!
(1)一站式学习Java网络编程 全面理解BIO/NIO/AIO
发文不易,请多多支持!如果帮助到了你,请点个赞!如果你急需代码,可以联系我!
热门评论
要是能有个源码的地址就更好了,哈哈?
请问有源码地址吗