前言:
在項目中應該會經常遇到導出word需求,Java中有5種方式導出word:
所用技術 | 優點 | 缺點 |
Jacob | 功能強大 | 代碼量大,設置樣式繁瑣;需要windows平臺支持,無法跨平臺 |
Apache POI | 讀寫excel功能強大、操作簡單 | 一般只用它讀取word,能夠創建簡單的word,不能設置樣式,功能太少 |
Java2word | 功能強大,操作簡單 | 能滿足一般要求,不支持07格式,國人開發的,參考資料較多,需要windows平臺支持 |
iText | 功能全,能滿足一般要求 | 不能直接生成或操作doc文檔,只能生成rtf格式的文檔,rtf也可以用word打開 |
JSP | 操作簡單,代碼量少 | 能把當前頁面導出簡單的word,不能設置樣式,美觀性差,無法操作word |
Freemarker | 代碼量少,樣式、內容容易控制,打印不變形,完全符合office標準 | 需要提前設計好word模板,把需要替換的地方用特殊標記標出來 |
具體選擇哪種方式實現Java導出word,要根據自己的需求和實際情況靈活選擇,今天這篇文章主要講解下java怎麼結合freemarker導出word的,我選擇freemarker主要因爲相對於poi導出word,它更靈活。
正文:
Freemarker導出word的思路是,先把word文件中插入特殊的字符串佔位符,另存爲xml,然後將xml翻譯爲FreeMarker模板,最後用java來解析FreeMarker模板,編碼調用FreeMarker實現文本替換並輸出Doc。
一、把插入佔位符word文件另存爲xml
網上很多說不建議用wps,但是我用的wps發現也沒有太大的影響,這是我word製作的表格。
然後另存爲Word XML文檔,把生成的xml放到WEB-INF目錄下,我在這個目錄下又建了word目錄,最後我放到word目錄下啦
直接打開xml會發現,格式很錯亂,代碼擠到一塊了,如下圖
我們可以通過idea格式化代碼的快捷鍵,把代碼格式化下,方便閱讀
Ctrl+Alt+L
二、引入Freemarker的maven依賴和lombok的依賴
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
三、編寫模板展示的類和導出的工具類以及測試類代碼
User類
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private String sex;
private String age;
private String phone;
private String email;
}
導出的工具類:
public class exportWord {
public void export(Map<String, Object> map) {
try {
Configuration configuration = new Configuration();
configuration.setDefaultEncoding("UTF-8");
//模板文件配置路徑
configuration.setDirectoryForTemplateLoading(new File("F:\\dao\\exportWord\\src\\main\\webapp\\WEB-INF\\word"));
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
//文件輸出路徑,文件名
File outFile = new File("E:\\測試模板.doc");
//掃描模板路徑下 模板文件
Template template = configuration.getTemplate("測試模板.xml", "UTF-8");
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"), 10240);
template.process(map, out);
out.flush();
out.close();
System.out.println("導出完成");
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
測試類:
public class Test {
public static void main(String[] args) {
exportWord ew = new exportWord();
Map<String,Object> dataMap = new HashMap<String,Object>();
List<User> list = new ArrayList<User>();
User user1 = new User("ada", "男", "18", "10000000", "[email protected]");
User user2 = new User("ada", "男", "18", "10000000", "[email protected]");
User user3 = new User("ada", "男", "18", "10000000", "[email protected]");
list.add(user1);
list.add(user2);
list.add(user3);
dataMap.put("userList", list);
ew.export(dataMap);
}
}
四、如果數據是集合,需要遍歷展示的
找到 xml文件中 <w:tr> 標籤 用 <#list map中鍵值 as 別名><w:tr></w:tr></#list> 標籤包裹在內即可
五、啓動測試類,看導出效果
六、 常見bug
順利的話,你應該會出現如下圖的bug,你只要格式化代碼啦,按照這個錯誤報的行數,去看下,就知道爲什麼了,原因是word轉xml的時候解析錯誤啦,需要去手動去調節下
freemarker.core.ParseException: Parsing error in template "測試模板.xml" in line 320, column 48:
Encountered "<", but was expecting one of:
<STRING_LITERAL>
<RAW_STRING>
"false"
"true"
<INTEGER>
<DECIMAL>
"."
"+"
"-"
"!"
"["
"("
"{"
<ID>
at freemarker.core.FMParser.generateParseException(FMParser.java:4672)
at freemarker.core.FMParser.jj_consume_token(FMParser.java:4543)
at freemarker.core.FMParser.UnaryExpression(FMParser.java:340)
at freemarker.core.FMParser.MultiplicativeExpression(FMParser.java:452)
at freemarker.core.FMParser.AdditiveExpression(FMParser.java:402)
at freemarker.core.FMParser.RangeExpression(FMParser.java:573)
at freemarker.core.FMParser.RelationalExpression(FMParser.java:528)
at freemarker.core.FMParser.EqualityExpression(FMParser.java:493)
at freemarker.core.FMParser.AndExpression(FMParser.java:602)
at freemarker.core.FMParser.OrExpression(FMParser.java:625)
at freemarker.core.FMParser.Expression(FMParser.java:238)
at freemarker.core.FMParser.StringOutput(FMParser.java:1076)
at freemarker.core.FMParser.Content(FMParser.java:2550)
at freemarker.core.FMParser.OptionalBlock(FMParser.java:2761)
at freemarker.core.FMParser.Root(FMParser.java:2933)
at freemarker.template.Template.<init>(Template.java:193)
at freemarker.cache.TemplateCache.loadTemplate(TemplateCache.java:419)
at freemarker.cache.TemplateCache.getTemplate(TemplateCache.java:330)
at freemarker.cache.TemplateCache.getTemplate(TemplateCache.java:205)
at freemarker.template.Configuration.getTemplate(Configuration.java:740)
at freemarker.template.Configuration.getTemplate(Configuration.java:681)
at com.util.exportWord.export(exportWord.java:25)
at com.util.test.Test.main(Test.java:54)
我們提前寫好的佔位符有時候轉完後會錯誤,被拆解了,只要把多餘的部分刪除就可以啦,我們看下<w:r></w:r>標籤是一個標籤,把多餘的刪掉即可,刪完後如下。
<w:tc>
<w:tcPr>
<w:tcW w:w="2027" w:type="dxa"/>
</w:tcPr>
<w:p>
<w:pPr>
<w:jc w:val="center"/>
<w:rPr>
<w:rFonts w:asciiTheme="minorEastAsia" w:hAnsiTheme="minorEastAsia"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
<w:szCs w:val="20"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:asciiTheme="minorEastAsia" w:hAnsiTheme="minorEastAsia"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
<w:szCs w:val="20"/>
</w:rPr>
<w:t>${user.name}</w:t>
</w:r>
</w:p>
</w:tc>
總結:
freemarker導出word還是很方便的,轉化後的xml咋看很複雜,其實認真看下,規律還是很明顯的,改動幾個保存下,你就試出作用啦。
我是阿達,一名喜歡分享知識的程序員,時不時的也會荒腔走板的聊一聊電影、電視劇、音樂、漫畫,這裏已經有373小夥伴在等你們啦,感興趣的就趕緊來點擊關注我把,哪裏有不明白或有不同觀點的地方歡迎留言。