官网:http://freemarker.foofun.cn/
一、简介
FreeMarker 是一款 模板引擎:即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
二、使用:
1、pom依赖
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.28</version> </dependency>
2、main方法执行(植入其他接口即可)
public class Export { public static void main(String[] args) throws IOException { ExportMyWord emw = new ExportMyWord(); Map<String, Object> dataMap = new HashMap<>(); UserInfo userInfo=new UserInfo(); userInfo.setName("傅先生").setAge("18").setSex("男").setPhone("13058074485").setEmail("1254874764@qq.com").setAddress("深圳龙华"); userInfo.setImage(emw.getImageStr("C:\\Users\\ruiec\\Desktop\\testFile\\logo2.jpg"));//头像 dataMap.put("userInfo", userInfo); ArrayList<Project> projectList = new ArrayList<>(); projectList.add(new Project().setTime( "2019/04 - 至今").setCompany("YZR科技有限公司").setDuty("Java资深大师")); projectList.add(new Project().setTime( "2017/6 - 2019/04").setCompany("GWYX科技有限公司").setDuty("Java小白")); dataMap.put("projectList", projectList); ArrayList<Education> educationList = new ArrayList<>(); educationList.add(new Education().setTime( "2014/09 - 2018/06").setSchoolName("贺XX院").setEducation("大学")); educationList.add(new Education().setTime( "2011/09 - 2014/06").setSchoolName("实XX学").setEducation("高中")); educationList.add(new Education().setTime( "2008/09 - 2011/06").setSchoolName("金XX中").setEducation("初中")); dataMap.put("educationList", educationList); String templateFile = "F:/res/MyGitHub/Tool/blog/src/main/java/com/ruiec/util/ExportMyWord/userInfo.ftl"; String exportFile = "F:/个人信息.doc"; emw.createWord(dataMap, templateFile, exportFile); } }
3、ExportMyWord类
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 保存文件路径的全路径名(路径+文件名) */ public void createWord(Map<String, Object> dataMap, String templateName, String saveFilePath) throws IOException { //加载模板(路径)数据 //config.setClassForTemplateLoading(this.getClass(), "");//相对路径 String ftlPath = templateName.substring(0, templateName.lastIndexOf("/")); config.setDirectoryForTemplateLoading(new File(ftlPath));//文件路径 //设置异常处理器 这样的话 即使没有属性也不会出错 如:${list.name}...不会报错 config.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER); Template template = null; if (templateName.endsWith(".ftl")) { templateName = templateName.substring(0, templateName.indexOf(".ftl")); } try { String ftlFile = templateName.substring(templateName.lastIndexOf("/")+1); template = config.getTemplate(ftlFile + ".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 */ 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()]; in.read(data); //注:FileInputStream.available()方法可以从输入流中阻断由下一个方法调用这个输入流中读取的剩余字节数 in.close(); } catch (IOException e) { log.error("IO操作图片错误", e); e.printStackTrace(); } BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(data); } }
4、方法2类:WordExportUtil
public class WordExportUtil { private static Logger LOGGER = LoggerFactory.getLogger(WordExportUtil.class); private static WordExportUtil service = null; public WordExportUtil() { super(); } public static WordExportUtil getInstance() { if(service == null) { synchronized(WordExportUtil.class){ if(service == null) { service = new WordExportUtil(); } } } return service; } /** * * @param templateFilePath eg: /template/test/test1.ftl * @param dataMap * @param exportFilePath eg: /tmp/test/test123.doc * @param loadType 设置路径加载方式。1-绝对路径,2-项目相对路径 * @return * @throws Exception */ public File createDocFile(String templateFilePath,Map<String, Object> dataMap, String exportFilePath, int loadType) throws Exception { Template t = null; Configuration configuration = new Configuration(Configuration.VERSION_2_3_28); configuration.setDefaultEncoding("UTF-8"); //设置异常处理器 这样的话 即使没有属性也不会出错 如:${list.name}...不会报错 configuration.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER); try { templateFilePath = pathReplace(templateFilePath); String ftlPath = templateFilePath.substring(0, templateFilePath.lastIndexOf("/")); if(loadType == 1) { configuration.setDirectoryForTemplateLoading(new File(ftlPath)); // FTL文件所存在的位置 }else { configuration.setClassForTemplateLoading(this.getClass(), ftlPath);//以类加载的方式查找模版文件路径 } String ftlFile = templateFilePath.substring(templateFilePath.lastIndexOf("/")+1); t = configuration.getTemplate(ftlFile); // 模板文件名 File outFile = new File(exportFilePath); Writer out = null; out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile))); t.process(dataMap, out); } catch (Exception e) { LOGGER.error("导出word文档出错", e); throw e; } return null; } /** * 把路径的\替换成/ * @param path * @return */ private String pathReplace(String path) { while(path != null && path.contains("\\")) { path = path.replace("\\", "/"); } return path; } }
5、fil文件
a、将制作好的word模板另存为xml文件
b、在IDEA中或者Notepad++等编辑器修改
单个字段:${name}
list循环:<#list educationList as education></w:tr>循环的值</#list>
坑点留意图片,xml先删除base64代码,然后使用${image}
<pkg:part pkg:name="/word/media/image1.png" pkg:contentType="image/png"> <pkg:binaryData>"base64编码"</pkg:binaryData> </pkg:part>
c、完成后直接修改后缀为:ftl