文章目录

  • 前言
  • 一、如何通过java代码生成PDF?
    • 1.依赖
  • 二、如何在PDF中画前台的echarts图?
      • 1.如何拿到前台echarts图的信息?
        • 前台代码:
    • 后台逻辑:生成PDF,并下载
    • PDFUtil 工具类如下:
  • PDF导出效果
  • 总结

前言

提示:本文仅用于记录日常,多有不足,仅供参考。

这两天遇到个任务,要将前台已有的echarts图表和一些数据整合,生成一个报表,且以PDF的格式自动下载保存。
前台的数据已经有了。这意味着,所有的数据源都可以直接拿到,那么现在要解决的问题只有以下3点:

  1. 如何通过java代码生成PDF?
  2. 如何在PDF中画前台的echarts图?
  3. 如何将生成的PDF自动下载下来?

一、如何通过java代码生成PDF?

1.依赖

查看了相关资料,和尝试,最终用来生成PDF的依赖只用了一个,如下:

<dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13.3</version>
</dependency>

二、如何在PDF中画前台的echarts图?

前面提到,所有的数据和图表的样子都已经再前台展示了。
那么,我的第一反应是,若我能直接从前台将echarts图的信息拿到,传给后台,再从后台将这个图片信息写到PDF中就可以啦。

1.如何拿到前台echarts图的信息?

我发现,前台在写echarts的时候,有一个配置项toolbox,当show:true设置其显示时,会看见一个下载按钮。点击后可以直接下载这个echarts图片。

前台代码:
var myChart1;
//加载实时调用
function loadChart1(res)
{var colorList = ['#73DDFF', '#73ACFF'];myChart1 = echarts.init(document.getElementById('chart-visitTrend'));option = {/* title: {text: '审计调用统计'}, */tooltip: {trigger: 'axis'},legend: {data: ['接收', '发送']},grid: {top:'5%',left: '1%',right: '3%',bottom: '0%',containLabel: true},/* toolbox: { //保存图片show: true,orient: "vertical",itemSize: 12,feature: {saveAsImage: {name:"实时调用下载"}}}, */xAxis: {type: 'category',boundaryGap: false,//data: ['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04', '2022-01-05', '2022-01-06', '2022-01-07']data:res.timeData},yAxis: {type: 'value',axisLabel: {formatter: '{value} 次'}},series: [{//name: '',type: 'line',stack: 'Total',color: colorList[0],//data: [120, 132, 101, 134, 90, 230, 210]data:res.countData}]};// 使用刚指定的配置项和数据显示图表。myChart1.clear();myChart1.setOption(option);
}

既然前台可以直接下载,说明一定可以直接从前台获取这个图片的信息。可以通过getDataURL()方法获取这个图片信息。直接看,前台“导出”按钮的点击事件。

// 导出
doExport = function()
{//获取echarts图的base64编码,为png格式。var picBase64Info = myChart1.getDataURL();var con = getCondition();//其它条件con['picBase64Info'] = picBase64Info;var form =document.createElement("form");form.style.display='none';form.action ='/reportform/exportform';form.method = 'post';document.body.append(form);for(var key in con){var input = document.createElement("input");input.type = "hidden";input.name = key;input.value = con[key];form.target = "exportFrame";form.appendChild(input);}form.submit();form.remove();// 以下通过ajax请求方式行不通,无法触发浏览器的下载动作
/*  $.ajax({url: '/reportform/exportform',type: "post",async: true,cache: false,data: JSON.stringify(con),contentType : "application/json",dataType: "json",error: function(xhr){// 操作失败}}); */
}

由上可见,picBase64Info 就是那张echarts图片经过base64加密后的信息。直接将它传递到后台,进行base64解密即可。忽略getCondition();操作,这只是我想携带的一些别的数据到后台。

提示: 由上面注释的内容可以看出,我没有直接通过ajax向后台发起请求的方式将图片信息携带到后台。原因是,这样浏览器无法触发下载动作。经尝试,使用一个隐形表单提交的形式,后台文件流可以直接下载。无需其它过多的处理。


后台逻辑:生成PDF,并下载

service部分代码如下(示例):

import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;@Overridepublic void exportForm(HttpServletResponse response, String picBase64Info){// 1.创建文档,设置文档页面大小,页边距Document document = new Document(PageSize.A4, Float.parseFloat("0"), Float.parseFloat("5"),Float.parseFloat("34"), Float.parseFloat("50"))// 2.解析前台传来的echart图,生成pdf段落元素Paragraph pictureEle = PDFUtil.createImageFromEncodeBase64(picBase64Info, "1、段落名称1", Float.parseFloat("34"),Float.parseFloat("50"), false);// 生成table表格段落(略去此处)// Paragraph table1 = PDFUtil.createTable(xx略, "2、段落名称2", new String[]{ "数据a1", "数据a2", "数据a3" }, xx略);// Paragraph table2 = PDFUtil.createTable(xx略, "3、段落名称3", xx略, xx略);// Paragraph table3  = PDFUtil.createTable(xx略,"4、段落名称4", xx略, xx略);// 将所有需要放到文档里的元素,汇总List<Paragraph> paragraphs = new ArrayList<Paragraph>();paragraphs.add(pictureEle);//paragraphs.add(table1);//paragraphs.add(table2);//paragraphs.add(table3);// 导出pdf文档:将paragraphs塞到document中,并下载documentPDFUtil.exportDocument(document, null, paragraphs, response, "报表");}

PDFUtil 工具类如下:

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;/*** ClassName : PDFUtil <br/>* Function : (生成PDF,工具类) <br/>* date : 2022年8月1日 上午9:42:11 <br/>** @author* @version * @since JDK 1.8*/
public class PDFUtil
{private static final Logger logger = LogManager.getLogger(PDFUtil.class);/*** fontSize_normal : (正文字体大小11号).*/public static final float FONTSIZE_NORMAL = Integer.parseInt("11");/*** fontSize_titile : (标题字体大小14号).*/public static final float FONTSIZE_TITILE = Integer.parseInt("14");/*** FONTSIZE_COVER : (封面字体大小32号).*/public static final float FONTSIZE_COVER = Integer.parseInt("32");/*** normalFont : (通用字体样式:宋体、11号).*/private static Font normalFont = null;/*** titleFont : (通用标题字体样式:宋体、14号、加粗).*/private static Font titleFont = null;/*** coverFont : (通用封面字体样式:宋体、28号、加粗).*/private static Font coverFont = null;/*** getBaseFont : (获取可以解析中文的字体:使用宋体). <br/>** @author * @return* @since JDK 1.8*/public static BaseFont getBaseFontChinese(){try{// 宋体资源文件路径,可以从C://Windows//Fonts//simsun.ttc拷贝到相应目录下URL path = PDFUtil.class.getResource("/config/fonts/simsun.ttc");return BaseFont.createFont(path + ",0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);// 本地测试:使用windows自带的宋体文件// return BaseFont.createFont("C://Windows//Fonts//simsun.ttc,0", BaseFont.IDENTITY_H, false);}catch (Exception e){logger.error("设置字体样式失败", e);return null;}}/*** getNormalFont : (获取普通字体样式). <br/>** @author * @return* @since JDK 1.8*/public static Font getNormalFont(){if (normalFont == null){BaseFont bfChinese = getBaseFontChinese();normalFont = new Font(bfChinese, FONTSIZE_NORMAL, Font.NORMAL);}return normalFont;}/*** getTitleFont : (获取标题通用字体). <br/>** @author * @return* @since JDK 1.8*/public static Font getTitleFont(){if (titleFont == null){BaseFont bfChinese = getBaseFontChinese();titleFont = new Font(bfChinese, FONTSIZE_TITILE, Font.BOLD);}return titleFont;}/*** getTitleFont : (获取封面通用字体). <br/>** @author * @return* @since JDK 1.8*/public static Font getCoverFontFont(){if (coverFont == null){BaseFont bfChinese = getBaseFontChinese();coverFont = new Font(bfChinese, FONTSIZE_COVER, Font.BOLD);}return coverFont;}/*** getfieldValue : (通过反射,根据方法名,执行方法,最终返回行结果的toString值). <br/>** @author * @param <T> 方法执行的入参* @param object 对象* @param methodName 方法名* @param args 方法执行参数* @since JDK 1.8*/private static <T> String getfieldValue(T object, String methodName, Object... args){try{Method method = object.getClass().getMethod(methodName);Object value = method.invoke(object, args);return value == null ? "" : value.toString();}catch (Exception e){logger.error("getfieldValue error", e);return "";}}/*** analysisPicBase64Info : (解析base64图片信息). <br/>** @author * @param picBase64Info 图片base64信息,或前台echart通过调用getDataURL()方法获取的图片信息* @return 图片经过base64解码后的信息* @since JDK 1.8*/public static Element analysisPicBase64Info(String picBase64Info){if (StringUtils.isEmpty(picBase64Info)){// 空段落return new Paragraph();}// 1.获取图片base64字符串信息:若入参是通过前台echarts调用getDataURL()方法获取的,则该字符串包含逗号,且则逗号后面的内容才是图片的信息String pictureInfoStr = picBase64Info.indexOf(",") == -1 ? picBase64Info : picBase64Info.split(",")[1];// 2.将图片信息进行base64解密byte[] imgByte = Base64.decodeBase64(pictureInfoStr);// 对异常的数据进行处理/*** .图片的原始表达ascii码范围是0-255,* .这里面有一些不可见的编码。然后为了图片正确传输才转成编码base64的0-63,* .当从base64转成byte时,byte的范围是[-128,127],那么此时就会可能产生负数,而负数不是在ascii的范围里,所以需要转换一下*/for (int i = 0; i < imgByte.length; i++){if (imgByte[i] < 0){imgByte[i] += 256;}}try{return Image.getInstance(imgByte);}catch (Exception e){logger.error("analysisPicBase64Info error", e);return new Paragraph();}}/*** analysisPicBase64Info_batch : (批量解析base64加密的图片信息,生成Image对象). <br/>** @author * @param picBase64Infos 经过base64加密的图片信息* @return* @since JDK 1.8*/public static List<Element> analysisPicBase64Info_batch(List<String> picBase64Infos){List<Element> images = new ArrayList<Element>();for (String li : picBase64Infos){Element image = analysisPicBase64Info(li);images.add(image);}return images;}/*** createImage : (根据图片的base64加密文件创建pdf图片). <br/>** @author * @param picBase64Info base64加密后的图片信息(支持台echart通过调用getDataURL()方法获取的图片信息)* @param title 段落标题* @param percentX 图片缩放比例X轴* @param percentY 图片缩放比例Y轴* @param titleCenter 标题是否居中,true-居中、false-默认居左* @return 返回图片段落* @since JDK 1.8*/public static Paragraph createImageFromEncodeBase64(String picBase64Info, String title, float percentX,float percentY, boolean titleCenter){// 1.获取图片Element element = analysisPicBase64Info(picBase64Info);// 2.创建段落,并添加标题Paragraph paragraph = new Paragraph(title, getTitleFont());// 空行paragraph.add(Chunk.NEWLINE);paragraph.add(Chunk.NEWLINE);if (titleCenter){// 居中paragraph.setAlignment(Element.ALIGN_CENTER);}else{// 居左setDefaultIndentationLeft(paragraph);}if (!(element instanceof Image)){// 图片解析失败return paragraph;}Image image = (Image) element;// 3.设置图片缩放比例image.scalePercent(percentX, percentY);// 4.图片放入该段落paragraph.add(image);return paragraph;}/*** createImageFromEncodeBase64_batch : (批量创建). <br/>** @author * @param picBase64Infos 图片base64加密后的信息(支持台echart通过调用getDataURL()方法获取的图片信息)* @param titles 段落标题* @param percentXs X轴缩放比例* @param percentYs Y轴缩放比例* @param titleCenter 标题是否居中* @return* @since JDK 1.8*/public static Paragraph createImageFromEncodeBase64_batch(List<String> picBase64Infos, List<String> titles,List<Float> percentXs, List<Float> percentYs, boolean titleCenter){Paragraph paragraphs = new Paragraph();for (int i = 0; i <= picBase64Infos.size(); i++){Paragraph imagePara = createImageFromEncodeBase64(picBase64Infos.get(i), titles.get(i), percentXs.get(i),percentYs.get(i), titleCenter);if (!imagePara.isEmpty()){paragraphs.add(imagePara);// 空行paragraphs.add(Chunk.NEWLINE);paragraphs.add(Chunk.NEWLINE);}}return paragraphs;}/*** createTable : (创建table段落). <br/>** @author * @param <T>* @param list 构建table的数据* @param title 该段落取的名字* @param methodNames 需要调用的方法名,用来获取单元格数据。通常是某个属性的get方法* @return* @since JDK 1.8*/public static <T> Paragraph createTable(List<T> list, String title, String[] tableHead, List<String> methodNames){return createTable(list, FONTSIZE_NORMAL, FONTSIZE_TITILE, title, tableHead, methodNames, false);}/*** createTable : (创建table段落). <br/>** @author * @param <T>* @param list 构建table的数据* @param title 该段落取的名字* @param methodNames 需要调用的方法名,用来获取单元格数据。通常是某个属性的get方法* @param titleCenter 标题是否居中:true-居中,false-居左* @return* @since JDK 1.8*/public static <T> Paragraph createTable(List<T> list, String title, String[] tableHead, List<String> methodNames,boolean titleCenter){return createTable(list, FONTSIZE_NORMAL, FONTSIZE_TITILE, title, tableHead, methodNames, titleCenter);}/*** createTableByList : (创建table段落). <br/>** @author * @param <T>* @param listData * @param normalFontSize 正文字体大小* @param titleFontSize 标题字体大小* @param title 段落名称* @param methodNames 获取表格属性的方法名* @param titleCenter 段落标题是否水平居中,true:需要居中;false:默认靠左* @return* @since JDK 1.8*/public static <T> Paragraph createTable(List<T> listData, float normalFontSize, float titleFontSize,String title, String[] tableHead, List<String> methodNames, boolean titleCenter){// 1.创建一个段落Paragraph paragraph = new Paragraph(title, getTitleFont());// 空行paragraph.add(Chunk.NEWLINE);paragraph.add(Chunk.NEWLINE);if (titleCenter){// 居中paragraph.setAlignment(Element.ALIGN_CENTER);}else{// 居左setDefaultIndentationLeft(paragraph);}// 3.创建一个表格PdfPTable table = new PdfPTable(methodNames.size());// 列数paragraph.add(table);// 4.构造表头for (String head : tableHead){head = StringUtils.isEmpty(head) ? "" : head;PdfPCell cell = new PdfPCell(new Paragraph(head, getNormalFont()));cell.setBackgroundColor(new BaseColor(Integer.parseInt("124"), Integer.parseInt("185"), Integer.parseInt("252")));// 背景色cell.setMinimumHeight(Float.parseFloat("15"));// 单元格最小高度cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 水平居中table.addCell(cell);}if (CollectionUtils.isEmpty(listData)){// 没有数据,添加一行空单元格,并返回for (int i = 0; i < methodNames.size(); i++){table.addCell(new Paragraph(" "));// 有一个空格,否则添加不了}return paragraph;}// 5.构造table数据for (T li : listData){for(String name : methodNames){String valueStr = getfieldValue(li, name);PdfPCell cell = new PdfPCell(new Paragraph(valueStr, getNormalFont()));cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 水平居中table.addCell(cell);}}// 5.返回return paragraph;}/*** addToTable : (从段落中找到第一个table,向该table中追加数据). <br/>* (). <br/>** @author * @param <T>* @param paragraph* @param listData* @param methodNames* @since JDK 1.8*/public static <T> void addToTable(Paragraph paragraph, List<T> listData, List<String> methodNames){for (Element ele : paragraph){if (!(ele instanceof PdfPTable)){// 不是table元素,直接跳过continue;}// 找到第一个table元素PdfPTable table = (PdfPTable) ele;for (T data : listData){for (String name : methodNames){String valueStr = getfieldValue(data, name);PdfPCell cell = new PdfPCell(new Paragraph(valueStr, getNormalFont()));cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 水平居中table.addCell(cell);}}break;}}/*** exportDocument : (生成并下载PDF文档). <br/>* (). <br/>** @author * @param document 文档对象* @param cover 封面:若不是null,则会先添加封面,并另起新页面添加段落* @param paragraphs 需要组成PDF文件的段落* @param response 请求的响应对象* @param fileName 生成的文件名称,不需要加pdf后缀* @since JDK 1.8*/public static void exportDocument(Document document, Paragraph cover, List<Paragraph> paragraphs,HttpServletResponse response, String fileName){try (ServletOutputStream out = response.getOutputStream()){response.setContentType("application/binary;charset=UTF-8");response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName + ".pdf", "UTF-8"));PdfWriter.getInstance(document, out);// 打开文档document.open();if (cover != null){document.add(cover);// 起新页面document.newPage();}StringBuilder errorMsg = new StringBuilder();for (int i = 0; i < paragraphs.size(); i++){try{// 将段落添加到文档document.add(paragraphs.get(i));// 空行document.add(Chunk.NEWLINE);document.add(Chunk.NEWLINE);}catch (DocumentException e){errorMsg.append("PDF文件生成出错,请检查第:").append(i).append("个段落");}}if (!StringUtils.isEmpty(errorMsg.toString())){logger.error(errorMsg);}// 关闭文档document.close();out.flush();out.close();}catch (Exception e){logger.error("生成PDF文档并下载,出错:", e);}}/*** setDefaultIndentationLeft : (设置段落标题默认左边距). <br/>** @author * @param paragraph* @param indentation* @since JDK 1.8*/public static void setDefaultIndentationLeft(Paragraph paragraph){paragraph.setIndentationLeft(Float.parseFloat("30"));}/*** addBlankLine : (添加空行). <br/>** @author * @param paragraph 需要添加空行的段落* @param lineNum 需要添加空行的个数* @since JDK 1.8*/public static void addBlankLine(Paragraph paragraph, int lineNum){if (paragraph == null){return;}for (int i = 0; i < lineNum; i++){paragraph.add(Chunk.NEWLINE);}}
}

PDF导出效果


总结

以上就是今天要讲的内容,简单介绍了JAVA如何创建PDF、如何将一张图片写入PDF、如何在前台拿到Echarts的图表。工具方法中提到了如何在PDF中创建表格。

【生成PDF】Java如何根据前台Echarts图表生成PDF,并下载相关推荐

  1. java Servlet mysql json ECharts图表生成实战-罗绍岗-专题视频课程

    java Servlet mysql json ECharts图表生成实战-2561人已学习 课程介绍         java WEB程序图表生成柱状图表.折线图表,echarts 动态获取Mysq ...

  2. echarts 图表导出PDF(带滚动条)/图片导出PDF

    echarts 图表导出PDF[带滚动条]/图片导出PDF 效果展示 提出问题 思考问题 解决问题 导出PDF 里面的页头中文乱码问题 参数说明 效果展示 提出问题 在开发过程中,有需求是将展示出来的 ...

  3. Java实现HighCharts纯后台图表生成

    上篇:强大的PhantomJS,牛逼的Espen Hovlandsdal HighCharts 是纯Js实现的.功能丰富的图表库,相关API访问链接 在HTML页面渲染好的HighCharts图表,可 ...

  4. Echarts 图表生成渐变色方法

    圈起部分为生成渐变色的内容,生成的效果见下图 根据需要可配置更多的颜色: 另外还有一种方法是使用Zrender插件的color.js

  5. echarts图表生成base64

    1. 首先我们先生成我们的图表 具体配置不详细写了 2.我们在点击的时候去获取我们的图表并转成base64格式 itemClick(){console.log(document.getElementB ...

  6. java excel导入前台_java后台生成了一个表格,用流传到前台,请问怎么接收呀?在线等...

    满意答案 soso2781 2017.06.13 采纳率:43%    等级:13 已帮助:10539人 ,这样单纯的拷贝文件是不可以的,必须要用到上传的组件, 常用的上传组件: Apache 的 C ...

  7. java 生成stub,Java工程使用axis的stub生成webservice客户端代码

    Axis2提供了一个wsdl2java.bat命令可以根据WSDL文件自动产生调用WebService的代码.wsdl2java.bat命令可以在/bin目录中找到. 在使用wsdl2java.bat ...

  8. php mysql echarts动态生成图表

    php mysql echarts动态生成图表,数据库和表格使用的java Servlet echarts 图表生成的一样数据,不知道的看我前面一篇博文:需要两个文件完成 1. 数据获取文件 week ...

  9. java后台生成含有echarts图表的报告并发送邮件

    无头浏览器使用 业务场景, 有一个报告的预览页面,这个页面中含有echarts图表,点击下载报告之后,调用echarts的getDataURL()方法将图片数据传到后台,在后台生成word或者pdf. ...

最新文章

  1. 手工接口测试考虑的点
  2. 福布斯2015中国非上市潜力企业100强
  3. 各种软路由 - 自制路由器
  4. 锁究竟锁住的是什么?
  5. 最全的时间类解析。 SimpleDateFormat + Date() 和 DateTimeFormatter + LocalDate()的区别与使用场景
  6. Java注解(1)-注解基础
  7. markdown 自用笔记
  8. 怎样写一篇优秀论文?看完受益匪浅!
  9. 在著名出版社出版书,你也行——记录我写书出版的经历和体会
  10. Linux之远程连接服务器ssh、telnet
  11. sqlite如何与mysql连接数据库连接_c#中怎么连接到sqlite数据库?
  12. Opencv 视频转为图像序列
  13. html5 选择收货地址,基于layer.js实现收货地址弹框选择然后返回相应的地址信息...
  14. java Base64带秘钥的加密解密
  15. windows 下 c++ 二维码生成库
  16. JS入门笔记九:循环精灵图案例
  17. Apache Solr任意文件读取漏洞复现
  18. jquery官网(jquery下载官网)
  19. linux命令行安装浏览器arm64,linux下安装google-chrome浏览器和chromedriver
  20. Git冲突:Your local changes would be overwritten by merge. Commit, stash or revert them to proceed.

热门文章

  1. hdfs文件误删恢复
  2. paddlepaddle框架小白入门级指南
  3. 速卖通知识产权规则介绍,如何才能规避侵权的问题?
  4. gdal_遥感影像配准
  5. 已开源!Flutter 基于分帧渲染的流畅度优化组件 Keframe
  6. 输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数
  7. 输入一行字符,统计其中有多少个单词,单词间用空格分隔(C语言)
  8. 对于计算机网络的感想,计算机网络实习心得
  9. HTML给整个框架插入背景图片
  10. python整合selenium爬取QQ空间访客记录