官網: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