聲明:轉載須要註明出處 http://www.javashuo.com/article/p-szsushnq-dc.html html
本篇是關於利用FreeMarker導出Word的實現步驟。java
優勢:採用FreeMarker是導出Word的最佳實現,很是的靈活,可以按照本身指定的樣式設置並輸出內容,操做簡單方便,代碼實現也容易。代碼量少,樣式、內容容易控制,打印不變形,徹底符合office標準web
缺點:須要提早設計好word模板,把須要替換的地方用特殊標記標出來緩存
關於使用POI的導出方案在另外一篇博客:http://www.javashuo.com/article/p-epbbdpfl-g.html 安全
下面是實現的效果圖:maven
下面是實現步驟:工具
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樣式,如圖是我繪製的模板:this
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中刪除圖片代碼就能夠了。
同時 但願各位能提出寶貴的意見方便改進 不甚感激!!