本地生成echarts图片并保存到pdf

一、所需工具

1、phantomjs

官网下载:http://phantomjs.org/download.html
国内镜像:http://npm.taobao.org/dist/phantomjs/

2、EChartsConvert
https://gitee.com/hbun_gao/echartsconvert-master

二、Maven依赖

		<!--模板引擎-->
		<dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
        </dependency>
        
        <!--用于发送http请求(调用本地echarts服务)-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.7</version>
        </dependency>
        
        <!--用于处理json数据-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.56</version>
        </dependency>
        
        <!--itext依赖-->
        <dependency>
            <groupId>com.itextpdf.tool</groupId>
            <artifactId>xmlworker</artifactId>
            <version>5.5.9</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf-itext5</artifactId>
            <version>9.0.3</version>
        </dependency>

三、运行EchartsConvert

./phantomjs-2.1.1-macosx/bin/phantomjs ./echartsconvert-master/echarts-convert.js -s -p 6666

echart服务拉下来就能用,同样的 phantomjs 解压就可以使用,如果长期需要使用的话,建议配置环境变量
phantomjs 配置到 bin 目录 echartsconvert-master配置到echartsconvert-master就可以了
我这里把这两个文件放到了同一个目录下,所以就可以直接就这样启动了,-p 映射下端口(尽量是不常用的)免得以后端口占用的时候忘记了

四、工具类

1.用于发送http请求

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
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;

public class HttpUtil {

    public static String post(String url, Map<String, String> params, String charset)
            throws ClientProtocolException, IOException {
        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();
        return responseEntity;
    }
}

2.Freemarker模板工具

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

public class FreemarkerUtil {
    private static final String path = FreemarkerUtil.class.getClassLoader().getResource("").getPath();

    public static String generateString(String templateFileName, String templateDirectory, Map<String, Object> datas)
            throws IOException, TemplateException {
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_0);

        // 设置默认编码
        configuration.setDefaultEncoding("UTF-8");
        // 设置模板所在文件夹
        configuration.setDirectoryForTemplateLoading(new File(path + templateDirectory));
        // 生成模板对象
        Template template = configuration.getTemplate(templateFileName);
        // 将datas写入模板并返回
        try (StringWriter stringWriter = new StringWriter()) {
            template.process(datas, stringWriter);
            stringWriter.flush();
            return stringWriter.getBuffer().toString();
        }
    }
}

3.生成Echarts工具类

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.client.ClientProtocolException;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class EchartsUtil {

//这里是你的服务地址
    private static String url = "http://localhost:6666";
    private static final String SUCCESS_CODE = "1";

    public static String generateEchartsBase64(String option) 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;
    }
}

4.EchartDto

@Data
@NoArgsConstructor
public class EchartsDto {

    private String templatePath;

    private Map<String, Object> datas;

    private String template;

    public EchartsDto(String template){
    //在这里配置你的模板路径(模板文件下面会有几个)
        this.templatePath = "";
        //这个是你模板的文件名
        this.template = template;
    }
}

5.把文件保存到本地

    /**
     * 生成图表
     * @param echarts
     * @return
     * @throws TemplateException
     * @throws IOException
     */
    public static String generateChart(EchartsDto echarts) throws TemplateException, IOException {
    //传入EchartsDto
        String option = FreemarkerUtil.generateString(echarts.getTemplate(), echarts.getTemplatePath(), echarts.getDatas());
        String base64 = EchartsUtil.generateEchartsBase64(option);
        //使用uuid 命名
        String uuid = UUID.randomUUID().toString();
        // 在这里的 ./是你项目的根目录
        String filePath = "./out/" + uuid + ".png";
        //生成本地图片
        generateImage(base64, filePath);
        return filePath;
    }
    public static void generateImage(String base64, String path) throws IOException {
        BASE64Decoder decoder = new BASE64Decoder();
        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();
        }
    }

五、模板以及测试

这里的传入数据都是自己封装的,具体情况可以看下echarts的官网以及里面的数据格式。
如果想直接测试用的话,可以先换成map
这里有两个注意的地方,单纯的字符串一定不能用json去转换,另一个是在模板里的字符串,一定要用单引号引起来

这里就先这么多了,其实都是大同小异,但是要注意一点,有的图表,官方的例子直接拿下来是用不了的,要把有些冗余的配置给删掉才可以,不然就会一直卡在请求上。建议是在官网上调整好了,然后把模复制到本地文件,注意不要最后的 “;” 和前面的哪些 只要 option=的大括号里面的内容

1.雷达图

radarOption.ftl
{
  title: {
    text: '${title}'
  },
  legend: {
    data: ${legendData}
  },
  radar: {
    indicator: ${indicator}
  },
  series: [
    {
      name: ${seriesName},
      type: 'radar',
      data:${data}
    }
  ]
}
public static EchartsDto generateChart1Pkg(LeidaChart leidaChart) {
        EchartsDto echarts = new EchartsDto("radarOption.ftl");
        HashMap<String, Object> datas = new HashMap<>();
        datas.put("title", leidaChart.getTitleText());
        datas.put("legendData",JSON.toJSONString(leidaChart.getLegendData()));
        datas.put("seriesName", JSON.toJSONString(leidaChart.getSeriesName()));
        datas.put("data", JSON.toJSONString(leidaChart.getSeriesData()));
        datas.put("indicator",JSON.toJSONString(leidaChart.getIndicator()));
        echarts.setDatas(datas);
        return echarts;
    }
//这里由于乌龙事件,代码是上面的里面是int 类型,下面的是double类型,其实用double类型的就可以了
public static EchartsDto generateChart1Pkg(LeidaDoubleChart leidaChart) {
        EchartsDto echarts = new EchartsDto("radarOption.ftl");
        HashMap<String, Object> datas = new HashMap<>();
        datas.put("title", leidaChart.getTitleText());
        datas.put("legendData",JSON.toJSONString(leidaChart.getLegendData()));
        datas.put("seriesName", JSON.toJSONString(leidaChart.getSeriesName()));
        datas.put("data", JSON.toJSONString(leidaChart.getSeriesData()));
        datas.put("indicator",JSON.toJSONString(leidaChart.getIndicator()));
        echarts.setDatas(datas);
        return echarts;
    }
    
  @Test
    void generateChart1PkgTest() throws Exception {
        LeidaChart leidaChart = new LeidaChart();
        leidaChart.setTitleText("Basic Radar Chart");
        leidaChart.setLegendData(Lists.newArrayList("1","2"));
        leidaChart.setIndicator(Lists.newArrayList(new IndicatorData("Sales",6500),new IndicatorData("Administration",16000),new IndicatorData("Information Technology",30000)));
        leidaChart.setSeriesName("Budget");
        leidaChart.setSeriesData(Lists.newArrayList(new SeriesData("Allocated Budget",Lists.newArrayList(4200, 3000, 20000))));
        GenerateChart.generateChart(GenerateChart.generateChart1Pkg(leidaChart));
    }

2.柱状图

barOption.ftl
 {
    title: {
        text:'${title}',
        x:'middle',
        textAlign:'center'
    },
    xAxis: {
        type: 'category',
        axisLabel: { show: true },
        data: ${categories}
    },
    yAxis: {
        type: 'value'
    },
    series: [{
        data: ${values},
        type: 'bar'
    }]
}
public static EchartsDto generateChart2Pkg(ZhuChart zhuChart) {
        EchartsDto echarts = new EchartsDto("barOption.ftl");
        HashMap<String, Object> datas = new HashMap<>();
        datas.put("categories", JSON.toJSONString(zhuChart.getXAxisData()));
        datas.put("values", JSON.toJSONString(zhuChart.getSeriesData()));
        datas.put("title","");
        if(Objects.nonNull( zhuChart.getTitle())){
            datas.put("title",zhuChart.getTitle());
        }
        echarts.setDatas(datas);
        return echarts;
    }
    @Test
    void generateChart2PkgTest() throws Exception {
        ZhuChart zhuChart = new ZhuChart();
        zhuChart.setTitle("水果");
        zhuChart.setXAxisData(Lists.newArrayList( "苹果", "香蕉", "西瓜"));
        zhuChart.setSeriesData(Lists.newArrayList(3, -2, 1));
        System.out.println(GenerateChart.generateChart(GenerateChart.generateChart2Pkg(zhuChart)));
    }

3.横向柱状图

{
  title: {
    text: '${title}'
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      type: 'shadow'
    }
  },
  xAxis: {
    type: 'value',
  },
  yAxis: {
    type: 'category',
    data: ${yaxis}
  },
  series: [
    {
      type: 'bar',
      data: ${seriesData}
    }
  ]
}
public static EchartsDto generateChart3Pkg(YZhuChart zhuChart) {
        EchartsDto echarts = new EchartsDto("TBarOption.ftl");
        HashMap<String, Object> datas = new HashMap<>();
        datas.put("yaxis", JSON.toJSONString(zhuChart.getYaxis()));
        datas.put("seriesData", JSON.toJSONString(zhuChart.getSeriesData()));
        datas.put("title", zhuChart.getTitleText());
        echarts.setDatas(datas);
        return echarts;
    }
    @Test
    void generateChart3PkgTest() throws Exception {
        YZhuChart zhuChart = new YZhuChart();
        zhuChart.setTitleText("World Population");
        zhuChart.setYaxis(Lists.newArrayList( "Brazil", "Indonesia", "USA"));
        zhuChart.setSeriesData(Lists.newArrayList(18203, 23489, 29034));
        System.out.println(GenerateChart.generateChart(GenerateChart.generateChart3Pkg(zhuChart)));
    }

4.饼状图

pieOption.ftl
 {
  title: {
    text: '${titleText}',
  },
  series: [
    {
      type: 'pie',
      data: ${seriesData},
    }
  ]
}
 public static EchartsDto generateChart4Pkg(PieChart pieChart) {
        EchartsDto echarts = new EchartsDto("pieOption.ftl");
        HashMap<String, Object> datas = new HashMap<>();
        datas.put("seriesData", JSON.toJSONString(pieChart.getSeriesData()));
        datas.put("titleText", pieChart.getTitleText());
        echarts.setDatas(datas);
        return echarts;
    }
    @Test
    void generateChart4PkgTest() throws Exception {
        PieChart pieChart = new PieChart();
        pieChart.setTitleText("Referer of a Website");
        pieChart.setSeriesData(Lists.newArrayList(new NameValuePair("Search Engine",1058),new NameValuePair("Direct",735)));
        System.out.println(GenerateChart.generateChart(GenerateChart.generateChart4Pkg(pieChart)));
    }

六、保存到pdf

 public static void generatePdf() throws Exception {
 		//文件的输出路径
   		String outPutPath = "./out/pdf/report/";
   		//图片的地址
   		Document document = new Document(PageSize.A4, 50, 50, 30, 20);
        File reportFile = new File(outPutPath);
        if (!reportFile.exists()) {
            reportFile.mkdirs();
        }
        String filePath = outPutPath + fileName + ".pdf";
        File pdfFile = new File(filePath);
        pdfFile.createNewFile();
        PdfWriter.getInstance(document, new FileOutputStream(filePath));
        document.open();
		imgPath = GenerateChart.generateChart(echarts);
		//使用段落加,会自己换行
		Paragraph p = new Paragraph();
		Image img = Image.getInstance(imgPath);
		img.setAlignment(Image.LEFT| Image.TEXTWRAP);
		//设置图片的大小(原大小(800,400))
		img.scaleToFit(400,200);
		//段落里用不了图片所以要使用chunk
		p.add(new Chunk(img, 0, 0, true));
		document.add(p);
		//记得关闭
		document.close();
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值