.personSunflowerP { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); border-top-left-radius: 7px; border-top-right-radius: 7px; color: rgba(255, 255, 255, 1); height: 1.8em; line-height: 1.8em; padding: 5px }

声明:转载需要注明出处  https://www.cnblogs.com/sun-flower1314/p/10126111.html

本篇是关于利用FreeMarker导出Word的实现步骤。

优点:采用FreeMarker是导出Word的最佳实现,非常的灵活,能够按照自己指定的样式设置并输出内容,操作简单方便,代码实现也容易。代码量少,样式、内容容易控制,打印不变形,完全符合office标准

缺点:需要提前设计好word模板,把需要替换的地方用特殊标记标出来

关于使用POI的导出方案在另一篇博客:https://www.cnblogs.com/sun-flower1314/p/10128796.html

下面是实现的效果图:

下面是实现步骤:

1.添加FreeMarker需要的jar包(这里用的是2.3.28版本,从网上的maven仓库中获取的)

  1. <dependency>
  2. <groupId>org.freemarker</groupId>
  3. <artifactId>freemarker</artifactId>
  4. <version>2.3.28</version>
  5. </dependency>

2.然后制作需要导出的Word模板。先利用office工具生成导出怎样的word样式,如图是我绘制的模板:

3.制作好了基本的样式之后,然后另存为.xml格式文档,如:

4.打开这个text.xml文件,在相应的地方填入${xx}表达式:

5.填好后,使用其Notepad++或Sublime工具打开文件,能够看到xml的内容如下:

填入后,如果有可能${}telephone 分离,则删除分离后${},然后在telephone上添加${}后保存。

  另一种最安全的方式是:不删除分离的${},先在telephone上添加${},保存后,用word工具打开test.xml,将原来分离的${}删除即可。

6.成功修改后,将文件重命名为.ftl格式的文件。然后将文件放置在项目中或其他路径。这里我是将其拷贝至包中

7. 接下来是代码层的实现

  1. package com.myHelloWorld;
  2.  
  3. import java.io.BufferedWriter;
  4. import java.io.File;
  5. import java.io.FileInputStream;
  6. import java.io.FileNotFoundException;
  7. import java.io.FileOutputStream;
  8. import java.io.IOException;
  9. import java.io.InputStream;
  10. import java.io.OutputStreamWriter;
  11. import java.io.Writer;
  12. import java.util.Map;
  13.  
  14. import freemarker.core.ParseException;
  15. import freemarker.log.Logger;
  16. import freemarker.template.Configuration;
  17. import freemarker.template.MalformedTemplateNameException;
  18. import freemarker.template.Template;
  19. import freemarker.template.TemplateException;
  20. import freemarker.template.TemplateExceptionHandler;
  21. import freemarker.template.TemplateNotFoundException;
  22. import sun.misc.BASE64Encoder;
  23.  
  24. /**
  25. * @Description 利用FreeMarker导出Word
  26. * 2018年12月15日 下午10:23:40
  27. * @Author Huang Xiaocong
  28. */
  29. public class ExportMyWord {
  30.  
  31. private Logger log = Logger.getLogger(ExportMyWord.class.toString());
  32. private Configuration config = null;
  33.  
  34. public ExportMyWord() {
  35. config = new Configuration(Configuration.VERSION_2_3_28);
  36. config.setDefaultEncoding("utf-8");
  37. }
  38. /**
  39. * FreeMarker生成Word
  40. * @param dataMap 数据
  41. * @param templateName 目标名
  42. * @param saveFilePath 保存文件路径的全路径名(路径+文件名)
  43. * @Author Huang Xiaocong 2018年12月15日 下午10:19:03
  44. */
  45. public void createWord(Map<String, Object> dataMap, String templateName, String saveFilePath) {
  46. //加载模板(路径)数据
  47. config.setClassForTemplateLoading(this.getClass(), "");
  48. //设置异常处理器 这样的话 即使没有属性也不会出错 如:${list.name}...不会报错
  49. config.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
  50. Template template = null;
  51. if(templateName.endsWith(".ftl")) {
  52. templateName = templateName.substring(0, templateName.indexOf(".ftl"));
  53. }
  54. try {
  55. template = config.getTemplate(templateName + ".ftl");
  56. } catch (TemplateNotFoundException e) {
  57. log.error("模板文件未找到", e);
  58. e.printStackTrace();
  59. } catch (MalformedTemplateNameException e) {
  60. log.error("模板类型不正确", e);
  61. e.printStackTrace();
  62. } catch (ParseException e) {
  63. log.error("解析模板出错,请检查模板格式", e);
  64. e.printStackTrace();
  65. } catch (IOException e) {
  66. log.error("IO读取失败", e);
  67. e.printStackTrace();
  68. }
  69. File outFile = new File(saveFilePath);
  70. if(!outFile.getParentFile().exists()) {
  71. outFile.getParentFile().mkdirs();
  72. }
  73. Writer out = null;
  74. FileOutputStream fos = null;
  75. try {
  76. fos = new FileOutputStream(outFile);
  77. } catch (FileNotFoundException e) {
  78. log.error("输出文件时未找到文件", e);
  79. e.printStackTrace();
  80. }
  81. out = new BufferedWriter(new OutputStreamWriter(fos));
  82. //将模板中的预先的代码替换为数据
  83. try {
  84. template.process(dataMap, out);
  85. } catch (TemplateException e) {
  86. log.error("填充模板时异常", e);
  87. e.printStackTrace();
  88. } catch (IOException e) {
  89. log.error("IO读取时异常", e);
  90. e.printStackTrace();
  91. }
  92. log.info("由模板文件:" + templateName + ".ftl" + " 生成文件 :" + saveFilePath + " 成功!!");
  93. try {
  94. out.close();//web项目不可关闭
  95. } catch (IOException e) {
  96. log.error("关闭Write对象出错", e);
  97. e.printStackTrace();
  98. }
  99. }
  100. /**
  101. * 获得图片的Base64编码
  102. * @param imgFile
  103. * @return
  104. * @Author Huang Xiaocong 2018年12月15日 下午10:15:10
  105. */
  106. public String getImageStr(String imgFile) {
  107. InputStream in = null;
  108. byte[] data = null;
  109. try {
  110. in = new FileInputStream(imgFile);
  111. } catch (FileNotFoundException e) {
  112. log.error("加载图片未找到", e);
  113. e.printStackTrace();
  114. }
  115. try {
  116. data = new byte[in.available()];
  117. //注:FileInputStream.available()方法可以从输入流中阻断由下一个方法调用这个输入流中读取的剩余字节数
  118. in.read(data);
  119. in.close();
  120. } catch (IOException e) {
  121. log.error("IO操作图片错误", e);
  122. e.printStackTrace();
  123. }
  124. BASE64Encoder encoder = new BASE64Encoder();
  125. return encoder.encode(data);
  126.  
  127. }
  128. }

下面是测试类:

  1. public static void main(String[] args) {
  2. ExportMyWord emw = new ExportMyWord();
  3. Map<String, Object> dataMap = new HashMap<String, Object>();
  4. dataMap.put("name", "黄xx");
  5. dataMap.put("age", 26);
  6. dataMap.put("blog", "sun_flower火柴客");
  7. dataMap.put("email", "sun_flower@xxxx.com");
  8. dataMap.put("gender", "男");
  9. dataMap.put("imgheader", emw.getImageStr("D:\\picture\\23.jpg"));
  10. dataMap.put("telephone", "123456789101");
  11. dataMap.put("address", "深圳");
  12. dataMap.put("naturework", "全职");
  13. dataMap.put("industry", "IT");
  14. dataMap.put("aplication", "Java开发");
  15. dataMap.put("time", "2013年-2017年");
  16. dataMap.put("schoolname", "南昌大学");
  17. dataMap.put("education", "本科");
  18. dataMap.put("projectname", "电子证照xxxx");
  19. dataMap.put("projecttime", "2017年3月");
  20. dataMap.put("projectcontent", "我们除了有视、听、味、嗅、触这些外感系统之外,人类还有一个非常重要的内感系统,就是我们情绪和情感的世界。"
  21. + "这种感受是那样地细腻、微妙、强烈、深沉;看不见、摸不着,说不清、道不明。...");
  22. emw.createWord(dataMap, "test.ftl", "E:/简历.doc");
  23. }

7.效果图:

整个过程就是这样。

对于需要多条记录或循环的部分,只要在模板层的代码中添加标签:

  1. <#list project as Item>
  2. <w:t>${Item.projectname}</w:t><w:br/>
  3. </#list>

这里说下需要注意的点:

1)很多项目中采用的是Log4j或 Commons Logging日志形式。而Freemarker自带日志类型,即:

  若导入的FreeMarker 2.3.x版本以下,可能回抛出Freemarker模版缓存问题:

  1. Compiling FreeMarker template test.ftl[zh_CN,UTF-8,parsed] ....
  2. Could not find template in cache

  看官方解释:

2)插入图片的时候格外小心,因为可能导出后是一堆图片代码,那是因为模板未能识别这个图片。说明导出没有问题,而是模板有问题。解决方案就是在原来的地方随便插入一张图片,然后在ftl中删除图片代码就可以了。

同时 希望各位能提出宝贵的意见方便改进 不甚感激!!

使用Freemarker导出Word文档(包含图片)代码实现及总结的更多相关文章

  1. freemarker导出word文档——WordXML格式解析

    前不久,公司一个项目需要实现导出文档的功能,之前是一个同事在做,做了3个星期,终于完成了,但是在项目上线之后却发现导出的文档有问题,此时,这个同事已经离职,我自然成为接班者,要把导出功能实现,但是我看 ...

  2. freemarker导出word文档

    使用freemarker导出word文档的过程 **************************************************************************** ...

  3. Java使用freemarker导出word文档

    通过freemarker,以及JAVA,导出word文档. 共分为三步: 第一步:创建模板文件 第二步:通过JAVA创建返回值. 第三步:执行 分别介绍如下: 第一步: 首先创建word文档,按照想要 ...

  4. Java用freemarker导出Word 文档

    1.用Microsoft Office Word打开word原件: 2.把需要动态修改的内容替换成***,如果有图片,尽量选择较小的图片几十K左右,并调整好位置: 3.另存为,选择保存类型Word 2 ...

  5. 使用FreeMarker导出word文档(支持导出图片)

    一.添加maven依赖,导入FreeMarker所需要的jar包 <dependency> <groupId>org.freemarker</groupId> &l ...

  6. Java 用Freemarker完美导出word文档(带图片)

    Java  用Freemarker完美导出word文档(带图片) 前言 最近在项目中,因客户要求,将页面内容(如合同协议)导出成word,在网上翻了好多,感觉太乱了,不过最后还是较好解决了这个问题. ...

  7. 【Java】用Freemarker完美导出word文档(带图片)

    Java  用Freemarker完美导出word文档(带图片) 前言 最近在项目中,因客户要求,将页面内容(如合同协议)导出成word,在网上翻了好多,感觉太乱了,不过最后还是较好解决了这个问题. ...

  8. 【Java】导出word文档之freemarker导出

    Java导出word文档有很多种方式,本例介绍freemarker导出,根据现有的word模板进行导出 一.简单导出(不含循环导出) 1.新建一个word文件.如下图: 2.使用word将文件另存为x ...

  9. PHP网页导出Word文档的方法分离

    今天要探讨的是PHP网页导出Word文档的方法,使用其他语言的朋友也可以参考,因为原理是差不多的. 原理 一般,有2种方法可以导出doc文档,一种是使用com,并且作为php的一个扩展库安装到服务器上 ...

随机推荐

  1. 98、配置ftp服务器(vsftpd)

    98.1.安装vsftpd: 1.安装: [root@m01 ~]# yum install -y vsftpd #安装vsftpd [root@m01 ~]# vsftpd -v #查看ftp的版本 ...

  2. JUnit5编写基本测试

    JUnit5的测试不是通过名称,而是通过注解来标识的. 测试类与方法 Test Class:测试类,必须包含至少一个test方法,包括: 最外层的class static member class @ ...

  3. Vue style与css的var()

    vue绑定style直接给css的var变量传递一个值,然后结合css的var()函数使用这个值. 在data里面定义一个变量然后给定一个值,后期修改这个值之后,所有依赖这个变量的css样式都会被响应 ...

  4. 用阻塞队列实现一个生产者消费者模型?synchronized和lock有什么区别?

    多线程当中的阻塞队列 主要实现类有 ArrayBlockingQueue是一个基于数组结构的有界阻塞队列,此队列按FIFO原则对元素进行排序 LinkedBlockingQueue是一个基于链表结构的 ...

  5. 资源:mysql下载路径

    mysql的下载路劲 https://dev.mysql.com/downloads/mysql/

  6. .Net5 IdentityServer4下SqlServer和Mysql数据迁移

    1.概念 以下概念从官网整理的,我也是看官网一步一步学习的 官网地址 https://identityserver4.readthedocs.io/en/latest/index.html 1.1 I ...

  7. external-resizer 源码分析/pvc 扩容分析

    kubernetes ceph-csi分析目录导航 基于tag v0.5.0 https://github.com/kubernetes-csi/external-resizer/releases/t ...

  8. Codeforces Round #707 Editorial Div2 题解

    CF1501 Div2 题解 CF1501A 这道题其实是一道英语阅读题,然后样例解释又不清晰,所以我看了好久,首先它告诉了你每个站点的预期到达时间 \(a_i\) ,以及每个站点的预期出发时间 \( ...

  9. C语言:类型转换

    1.自动类型转换:将小范围数据类型转换为大范围的数据类型 2.赋值号两边的数据类型不一致时,会自动将右边的数据类型转换为左边的数据类型.若右边数据的类型级别高,则根据左边变量的长度截取低字节数据部分 ...

  10. 关于torch.nn.LSTM()的输入和输出

    主角torch.nn.LSTM() 初始化时要传入的参数 | Args: | input_size: The number of expected features in the input `x` ...