声明:转载需要注明出处 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仓库中获取的)
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.28</version> </dependency>
2.然后制作需要导出的Word模板。先利用office工具生成导出怎样的word样式,如图是我绘制的模板:
3.制作好了基本的样式之后,然后另存为.xml格式文档,如:
4.打开这个text.xml文件,在相应的地方填入${xx}表达式:
5.填好后,使用其Notepad++或Sublime工具打开文件,能够看到xml的内容如下:
填入后,如果有可能${}与 telephone 分离,则删除分离后${},然后在telephone上添加${}后保存。
另一种最安全的方式是:不删除分离的${},先在telephone上添加${},保存后,用word工具打开test.xml,将原来分离的${}删除即可。
6.成功修改后,将文件重命名为.ftl格式的文件。然后将文件放置在项目中或其他路径。这里我是将其拷贝至包中
7. 接下来是代码层的实现
package com.myHelloWorld; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Map; import freemarker.core.ParseException; import freemarker.log.Logger; import freemarker.template.Configuration; import freemarker.template.MalformedTemplateNameException; import freemarker.template.Template; import freemarker.template.TemplateException; import freemarker.template.TemplateExceptionHandler; import freemarker.template.TemplateNotFoundException; import sun.misc.BASE64Encoder; /** * @Description 利用FreeMarker导出Word * 2018年12月15日 下午10:23:40 * @Author Huang Xiaocong */ public class ExportMyWord { private Logger log = Logger.getLogger(ExportMyWord.class.toString()); private Configuration config = null; public ExportMyWord() { config = new Configuration(Configuration.VERSION_2_3_28); config.setDefaultEncoding("utf-8"); } /** * FreeMarker生成Word * @param dataMap 数据 * @param templateName 目标名 * @param saveFilePath 保存文件路径的全路径名(路径+文件名) * @Author Huang Xiaocong 2018年12月15日 下午10:19:03 */ public void createWord(Map<String, Object> dataMap, String templateName, String saveFilePath) { //加载模板(路径)数据 config.setClassForTemplateLoading(this.getClass(), ""); //设置异常处理器 这样的话 即使没有属性也不会出错 如:${list.name}...不会报错 config.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER); Template template = null; if(templateName.endsWith(".ftl")) { templateName = templateName.substring(0, templateName.indexOf(".ftl")); } try { template = config.getTemplate(templateName + ".ftl"); } catch (TemplateNotFoundException e) { log.error("模板文件未找到", e); e.printStackTrace(); } catch (MalformedTemplateNameException e) { log.error("模板类型不正确", e); e.printStackTrace(); } catch (ParseException e) { log.error("解析模板出错,请检查模板格式", e); e.printStackTrace(); } catch (IOException e) { log.error("IO读取失败", e); e.printStackTrace(); } File outFile = new File(saveFilePath); if(!outFile.getParentFile().exists()) { outFile.getParentFile().mkdirs(); } Writer out = null; FileOutputStream fos = null; try { fos = new FileOutputStream(outFile); } catch (FileNotFoundException e) { log.error("输出文件时未找到文件", e); e.printStackTrace(); } out = new BufferedWriter(new OutputStreamWriter(fos)); //将模板中的预先的代码替换为数据 try { template.process(dataMap, out); } catch (TemplateException e) { log.error("填充模板时异常", e); e.printStackTrace(); } catch (IOException e) { log.error("IO读取时异常", e); e.printStackTrace(); } log.info("由模板文件:" + templateName + ".ftl" + " 生成文件 :" + saveFilePath + " 成功!!"); try { out.close();//web项目不可关闭 } catch (IOException e) { log.error("关闭Write对象出错", e); e.printStackTrace(); } } /** * 获得图片的Base64编码 * @param imgFile * @return * @Author Huang Xiaocong 2018年12月15日 下午10:15:10 */ public String getImageStr(String imgFile) { InputStream in = null; byte[] data = null; try { in = new FileInputStream(imgFile); } catch (FileNotFoundException e) { log.error("加载图片未找到", e); e.printStackTrace(); } try { data = new byte[in.available()]; //注:FileInputStream.available()方法可以从输入流中阻断由下一个方法调用这个输入流中读取的剩余字节数 in.read(data); in.close(); } catch (IOException e) { log.error("IO操作图片错误", e); e.printStackTrace(); } BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(data); } }
下面是测试类:
public static void main(String[] args) { ExportMyWord emw = new ExportMyWord(); Map<String, Object> dataMap = new HashMap<String, Object>(); dataMap.put("name", "黄xx"); dataMap.put("age", 26); dataMap.put("blog", "sun_flower火柴客"); dataMap.put("email", "sun_flower@xxxx.com"); dataMap.put("gender", "男"); dataMap.put("imgheader", emw.getImageStr("D:\\picture\\23.jpg")); dataMap.put("telephone", "123456789101"); dataMap.put("address", "深圳"); dataMap.put("naturework", "全职"); dataMap.put("industry", "IT"); dataMap.put("aplication", "Java开发"); dataMap.put("time", "2013年-2017年"); dataMap.put("schoolname", "南昌大学"); dataMap.put("education", "本科"); dataMap.put("projectname", "电子证照xxxx"); dataMap.put("projecttime", "2017年3月"); dataMap.put("projectcontent", "我们除了有视、听、味、嗅、触这些外感系统之外,人类还有一个非常重要的内感系统,就是我们情绪和情感的世界。" + "这种感受是那样地细腻、微妙、强烈、深沉;看不见、摸不着,说不清、道不明。..."); emw.createWord(dataMap, "test.ftl", "E:/简历.doc"); }
7.效果图:
整个过程就是这样。
对于需要多条记录或循环的部分,只要在模板层的代码中添加标签:
<#list project as Item> <w:t>${Item.projectname}</w:t><w:br/> </#list>
这里说下需要注意的点:
1)很多项目中采用的是Log4j或 Commons Logging日志形式。而Freemarker自带日志类型,即:
若导入的FreeMarker 2.3.x版本以下,可能回抛出Freemarker模版缓存问题:
Compiling FreeMarker template test.ftl[zh_CN,UTF-8,parsed] ....
Could not find template in cache
看官方解释:
2)插入图片的时候格外小心,因为可能导出后是一堆图片代码,那是因为模板未能识别这个图片。说明导出没有问题,而是模板有问题。解决方案就是在原来的地方随便插入一张图片,然后在ftl中删除图片代码就可以了。
同时 希望各位能提出宝贵的意见方便改进 不甚感激!!