@
首先先建立一個word檔案,輸入模板內容freemaker的內容,下面是本次演示的word檔案。
然後將word檔案另存為 .xml
檔案,然後再把檔案字尾改成.ftl
。將專案的resource目錄下建立一個templates目錄(非必須步驟)將 模板檔案放到templates目錄下
開啟模板檔案按 Ctrl + Shift + L 將模板內容格式化。
處理文字比較簡單,將需要替換文字中直接用預留位置 ${}
替換即可。
這裡發現一個問題因為之前在word格式時我就已經替換了變數,但是在ftl變數卻被 拆分成多段了(見圖1)。但是這樣是freemaker是無法成功替換變數的,所以需要手動處理成到一個段裡(如圖2),關於這點實在太無語了,因為沒有找到比較好的處理辦法,只能手工處理,在實際的開發工作中曾經花了幾個小時來做這件事情。
圖1:
圖2
如果模板裡需要用變數填充表格,建議模板裡的表格像word檔案一樣建一個兩行的表格。在模板中<<w:tbl>> 表示一個表格 、<w: tr> 表示一行、<w: tc> 表示一列。因為FreeMarker 是利用列表一行一行迴圈填充的,所以我們可以根據關鍵字找到<<w:tbl>>標籤,因為第一個 <w: tr>是表頭注意不要改到了,找到第二個<w: tr>在前後分別加上如下語句即可,後面的表格裡後面的行<w: tr>需要刪掉,建議模板裡的表格像word檔案一樣建一個兩行的表格即可這樣就不用刪了:
<#list itemList as item>
</#list>
替換後的模板如下:
如果模板裡需要用變數填充圖片,建議先在word檔案裡插入一張圖片,這樣在模板檔案裡找到<pkg:binaryData>標籤直接裡面把裡面的圖片base64字元替換成變數即可,word裡可以通過植入base64字元來展示圖片:
替換前:
替換後:
<pkg:binaryData>${image1}</pkg:binaryData>
到此模板已經調整完成,接下來就可以開始寫程式碼了。
在專案的pom檔案裡引入如下依賴
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
將圖片轉成Base64字串的公共方法:
public static String getImageBase64Str(String imgFile) {
try( InputStream in = new FileInputStream(imgFile)) {
byte[] data = new byte[in.available()];
in.read(data);
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
根據模板檔案生成word,主要生成的word的檔案字尾必須是doc不能是docx,不然生成的檔案無法開啟。
public static void crateWord(Map<String, Object> dataMap, String templatePath, String targetFile){
String path = templatePath.substring(0,templatePath.lastIndexOf("/"));
String templateName = templatePath.substring(templatePath.lastIndexOf("/") + 1);
try (FileOutputStream out = new FileOutputStream(targetFile);
Writer writer = new BufferedWriter(new OutputStreamWriter(out, "utf-8"))){
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
configuration.setDefaultEncoding("utf-8");
configuration.setClassForTemplateLoading(FreeMakerTest.class, path);
//除了ClassForTemplateLoading外,另一種模板載入方式DirectoryForTemplateLoading,也可用
//ClassPathResource resource = new ClassPathResource(path);
//configuration.setDirectoryForTemplateLoading(resource.getFile());
//載入模板
Template template = configuration.getTemplate(templateName);
//渲染模板
template.process(dataMap, writer);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (TemplateException e) {
throw new RuntimeException(e);
}
}
新建的列表資料實體類:
public class Arrears{
private String name;
private Integer num;
private String endDay;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public String getEndDay() {
return endDay;
}
public void setEndDay(String endDay) {
this.endDay = endDay;
}
}
準備模板填充資料
private static Map<String, Object> prepareParam(){
LocalDate currentDate = LocalDate.now();
LocalDate endDate = currentDate.plusYears(1L);
List<Arrears> arrearsList = new ArrayList<>();
arrearsList.add(new Arrears(){{setName("一頓老魏");setNum(1);setEndDay("三月內");}});
arrearsList.add(new Arrears(){{setName("貴州大黃牛");setNum(1);setEndDay("一年內");}});
arrearsList.add(new Arrears(){{setName("v我50");setNum(1);setEndDay("一月內");}});
//填充所需要的資料
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("debtor", "陳有楚");
dataMap.put("nowYear", String.valueOf(currentDate.getYear()));
dataMap.put("nowMonth", String.valueOf(currentDate.getMonthValue()));
dataMap.put("nowDay", String.valueOf(currentDate.getDayOfMonth()));
dataMap.put("arrears", "一頓老魏、貴州大黃牛、v我50");
dataMap.put("endYear", String.valueOf(endDate.getYear()));
dataMap.put("endMonth", String.valueOf(endDate.getMonthValue()));
dataMap.put("endDay", String.valueOf(endDate.getDayOfMonth()));
dataMap.put("arrearsList", arrearsList);
dataMap.put("image1", getImageBase64Str("D:\\picture\\其他\\24-05-23-142418.png"));
dataMap.put("creditor", "知北遊");
return dataMap;
}
測試程式碼:
public static void main(String[] args) throws IOException {
//準備引數
Map<String, Object> dataMap = prepareParam();
crateWord(dataMap,"/templates/qiantiao.ftl","D:\\test\\qiantiao.doc");
}
測試結果: