文档操作
Word模板替换
引入依赖
使用Easypoi,文档地址:https://www.yuque.com/guomingde/easypoi
模板是处理复杂Excel或Word的简单方法,EasyPoi支持各种指令,最主要的就是各种fe的用法,整体风格和el表达式类似。
采用的写法是{{}}
代表表达式,然后根据表达式里面的数据取值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
<dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-web</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-annotation</artifactId> <version>4.4.0</version> </dependency>
<dependency> <groupId>org.apache.poi</groupId> <artifactId>ooxml-schemas</artifactId> <version>1.4</version> </dependency>
<dependency> <groupId>org.docx4j</groupId> <artifactId>docx4j-export-fo</artifactId> <version>6.1.0</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> </exclusions> </dependency>
<dependency> <groupId>com.github.jai-imageio</groupId> <artifactId>jai-imageio-jpeg2000</artifactId> <version>1.3.0</version> </dependency>
|
Word模板工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
@Component public class WordUtil {
@Value(value = "${file.path}") private String uploadpath; @Autowired private ResourceLoader resourceLoader; public String exportPdf(WordTemplateEnum templateType, Map<String, Object> params) throws IOException { String temPath = templateType.getPath(); String absolutePath = resourceLoader.getResource("classpath:" + temPath).getFile().getAbsolutePath(); return exportPdf(absolutePath, params); }
public String exportPdf(String templatePath, Map<String, Object> params) { String woldFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".docx"; String saveDir = uploadpath + File.separator + "pdf"; String pdfFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".pdf"; String word = exportWord(templatePath, saveDir, woldFileName, params); Assert.notNull(word, "word路径不能为空"); String pdfPath = saveDir + File.separator + pdfFileName;
WordToPdfUtil.word2Pdf(word, pdfPath); return pdfPath; }
public String exportWord(String templatePath, String saveDir, String fileName, Map<String, Object> params) { Assert.notNull(templatePath, "模板路径不能为空"); Assert.notNull(saveDir, "临时文件路径不能为空"); Assert.notNull(fileName, "导出文件名不能为空"); Assert.isTrue(fileName.endsWith(".docx"), "word导出请使用docx格式"); if (!saveDir.endsWith("/")) { saveDir = saveDir + File.separator; } File dir = new File(saveDir); if (!dir.exists()) { dir.mkdirs(); } String savePath = saveDir + fileName; try { XWPFDocument doc = WordExportUtil.exportWord07(templatePath, params); FileOutputStream fos = new FileOutputStream(savePath); doc.write(fos); fos.flush(); fos.close(); } catch (Exception e) { e.printStackTrace(); } return savePath; } }
|
Word转PDF
引入依赖
主要引入本地的jar包,使用的是Aspose.pdf
和 Aspose.words
,Aspose为商用依赖,需要证书以及破解才能完美使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <dependency> <groupId>com.aspose-pdf-cracked</groupId> <artifactId>aspose-pdf-cracked</artifactId> <scope>system</scope> <version>1.0</version> <systemPath>${basedir}/lib/aspose-pdf-22.4.cracked.jar</systemPath> </dependency> <dependency> <groupId>com.aspose-pdf</groupId> <artifactId>aspose-pdf</artifactId> <scope>system</scope> <version>1.0</version> <systemPath>${basedir}/lib/aspose-pdf-22.4.jar</systemPath> </dependency> <dependency> <groupId>com.aspose.words</groupId> <artifactId>aspose-words</artifactId> <version>15.8.0</version> <scope>system</scope> <systemPath>${basedir}/lib/aspose-words-15.8.0-jdk16.jar</systemPath> </dependency>
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>fontbox</artifactId> <version>2.0.9</version> </dependency>
|
Word转PDF工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class WordToPdfUtil {
public static void word2Pdf(String wordPath, String pdfPath){ FileOutputStream os =null; try { String s = "<License><Data><Products><Product>Aspose.Total for Java</Product><Product>Aspose.Words for Java</Product></Products><EditionType>Enterprise</EditionType><SubscriptionExpiry>20991231</SubscriptionExpiry><LicenseExpiry>20991231</LicenseExpiry><SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber></Data><Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature></License>"; ByteArrayInputStream is = new ByteArrayInputStream(s.getBytes()); License license = new License(); license.setLicense(is);
File file = new File(pdfPath); os = new FileOutputStream(file); Document doc = new Document(wordPath); doc.save(os, SaveFormat.PDF); } catch (Exception e) { e.printStackTrace(); }finally{ if(os != null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
|
Echarts后台生成
引入依赖
大佬封装的一套后台是直接使用Echarts的工具,官网文档:https://echarts.icepear.org/#/zh-cn/quick-start
1 2 3 4 5 6 7 8 9 10 11 12
| <dependency> <groupId>org.icepear.echarts</groupId> <artifactId>echarts-java</artifactId> <version>1.0.7</version> <exclusions> <exclusion> <artifactId>slf4j-simple</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency>
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public class FirstTest { public static void main(String[] args) {
test02(); }
private static void test02() { Line lineChart = new Line() .addXAxis(new CategoryAxis() .setData(new String[] { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }) .setBoundaryGap(false)) .addYAxis() .addSeries(new LineSeries() .setData(new Number[] { 820, 932, 901, 934, 1290, 1330, 1320 }) .setAreaStyle(new LineAreaStyle()));
Option option = lineChart.getOption(); option.setAnimation(false); Engine engine = new Engine(); String jsonStr = engine.renderJsonOption(lineChart); String htmlStr = engine.renderHtml(option); }
private static void test01() { Bar bar = new Bar() .setLegend() .setTooltip("item") .addXAxis(new String[] { "Matcha Latte", "Milk Tea", "Cheese Cocoa", "Walnut Brownie" }) .addYAxis() .addSeries("2015", new Number[] { 43.3, 83.1, 86.4, 72.4 }) .addSeries("2016", new Number[] { 85.8, 73.4, 65.2, 53.9 }) .addSeries("2017", new Number[] { 93.7, 55.1, 82.5, 39.1 }); Engine engine = new Engine(); bar.getOption().setAnimation(false); String str = engine.renderHtml(bar); engine.render("index.html", bar); } }
|
Freemarker模板使用
引入依赖
参考博客:https://blog.csdn.net/hyc123123123123/article/details/133886078
1 2 3 4 5 6
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> <version>2.5.1</version> </dependency>
|
Freemarker工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class FreemarkerTool { private static final Logger log = LoggerFactory.getLogger(FreemarkerTool.class);
public static void data2html(String sourceFile, Map<String, Object> data, String destFile) throws IOException, TemplateException {
File destFolder = new File(destFile).getParentFile(); if (!destFolder.exists()) { destFolder.mkdirs(); }
Writer out = null; try { out = new FileWriter(destFile); Configuration cfg = new Configuration(Configuration.VERSION_2_3_29); cfg.setDirectoryForTemplateLoading(new File("C:/Users/Lenovo/Desktop/export/template")); Template template = cfg.getTemplate(sourceFile); template.process(data, out); } catch (Exception e) { log.error("模板生成报告html文件异常", e); throw e; } finally { try { if (out != null) { out.flush(); out.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
|
Echarts模板
新建文件echart-test.ftl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| <html>
<head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>ECharts Demo</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.2.2/echarts.min.js" integrity="sha512-ivdGNkeO+FTZH5ZoVC4gS4ovGSiWc+6v60/hvHkccaMN2BXchfKdvEZtviy5L4xSpF8NPsfS0EVNSGf+EsUdxA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <style> body { margin: 0; display: flex; flex-direction: row; justify-content: center; } #display-container { width: 600px; height: 600px; border: 2px solid black; } </style> </head>
<body> <div id="container">
<div id="display-container"> </div> </div>
<script type="text/javascript">
var chart = echarts.init(document.getElementById("display-container")); var option = { "animation": false,
"xAxis": { "type": "category", "axisTick": { "alignWithLabel": true }, "data": ${xAxisData!'[]'} }, "yAxis": { "type": "value" }, "tooltip": { "axisPointer": { "type": "shadow" }, "trigger": "axis" }, "series": [ { "type": "bar", "name": "Direct", "data": ${yAxisData!'[]'}, "barWidth": "60%" } ] } chart.setOption(option); </script> </body>
</html>
|
使用
使用数据替换模板中的标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class FreemarkerTest { public static void main(String[] args) throws IOException, TemplateException { String sourceFile = "echart-test.ftl"; Map<String, Object> datas = new HashMap<String, Object>(); List<String> xAxisData = ListUtil.of("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"); datas.put("xAxisData", JSON.toJSONString(xAxisData)); List<Integer> yAxisData = ListUtil.of(10, 52, 200, 334, 390, 330, 220); datas.put("yAxisData", JSON.toJSONString(yAxisData)); String destFile = "C:/Users/Lenovo/Desktop/export/echart-test-" + System.currentTimeMillis() + ".html";
FreemarkerTool.data2html(sourceFile, datas, destFile);
} }
|
Html转图片
下载wkhtmltoimage
wkhtmltopdf,wkhtmltoimage是开源命令行工具,可以将html转为pdf或者image。
官网:https://wkhtmltopdf.org/downloads.html
原理:wkhtmltopdf&wkhtmltoimage 内嵌了一个QT浏览器,其原理是会使用该内嵌的浏览器打开html文件或链接,然后对该网页进行截图处理。
注意:导出的图片或pdf空白由于wkhtmltopdf&wkhtmltoimage 0.12.6 最新版发布于 2020-7-11, 其使用的QT浏览器由于版本比较旧,可能会无法识别较新版本的JavaScript语法,比如我们使用的Echarts组件,需要更改Style样式,以及通过Option取消Echarts的动画。
Html转图片工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class HtmlToImageUtil {
private static String TO_IMG_TOOL = "D:/DevTools/wkhtmltopdf/bin/wkhtmltoimage.exe";
public static boolean htmlToImg(String srcPath, String destPath, Integer width) { File file = new File(destPath); File parent = file.getParentFile(); if (!parent.exists()) { parent.mkdirs(); } StringBuilder cmd = new StringBuilder(); cmd.append(TO_IMG_TOOL); cmd.append(" "); cmd.append(" --format png "); cmd.append(" --quality 75 ");
if (width != null) { cmd.append(" --width "); cmd.append(width); }
cmd.append(" "); cmd.append(srcPath); cmd.append(" "); cmd.append(destPath);
boolean result = true; try { Process proc = Runtime.getRuntime().exec(cmd.toString()); HtmlToPdfThread error = new HtmlToPdfThread(proc.getErrorStream()); HtmlToPdfThread output = new HtmlToPdfThread(proc.getInputStream()); error.start(); output.start(); proc.waitFor(); } catch (Exception e) { result = false; e.printStackTrace(); } return result; } }
|
最终需求
报表功能
定时生成报表,比如日报,周报,月报等数据。
- 查询数据库,将数据通过Word模板展示。
- 使用Eeasypoi,可以将数据和图片插入到Word模板中。
- 统计分析,将统计分析的数据生成Echarts图表,并插入到Word中。
- 使用echarts-java,将统计分析的数据生成图表Html文件。
- 或者使用Freemarker,通过模板生成图标Html文件。
- 使用wkhtmltoimage,将Html文件转为图片。
- 预览,将Word转成PDF,以便浏览器预览。
- 使用Aspose.pdf,将Word转为PDF文档。