Java Web專案中使用Freemarker生成Word文件
Web專案中生成Word文件的操作屢見不鮮,基於Java的解決方案也是很多的,包括使用Jacob、Apache POI、Java2Word、iText等各種方式,其實在從Office 2003開始,就可以將Office文件轉換成XML檔案,這樣只要將需要填入的內容放上${}佔位符,就可以使用像Freemarker這樣的模板引擎將出現佔位符的地方替換成真實資料,這種方式較之其他的方案要更為簡單。
下面舉一個簡單的例子,比如在Web頁面中填寫個人簡歷,然後點選儲存下載到本地,效果圖如下所示。
開啟下載的Word檔案
首先在Eclipse Java EE版中新建一個Dynamic Web Project,專案結構如下圖所示
需要向專案中加入freemarker的JAR檔案,可以通過下面的連結獲得Freemarker的最新版本:
模板檔案resume.ftl是如何生成的呢,其實非常簡單,將需要的Word文件做好之後,選擇另存為XML檔案,另存之後建議用Editplus、Notepad++、Sublime等工具開啟檢視一下,因為有的時候你寫的佔位符可能會被拆開,這樣Freemarker就無法處理了。
開啟XML檔案看看吧,如果剛才你寫的${title}、${name}被xml檔案給拆散了,修改一下XML檔案就OK了。
修改過後另存為resume.ftl模板檔案,如下所示:
接下來就是Servlet(也可以是Struts2的Action、Spring MVC的Controller等)和工具類WordGenerator的編寫以及頁面test.jsp的製作了,程式碼如下所示:
小服務的程式碼:
package com.lovo.servlet; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.lovo.util.WordGenerator; /** * Servlet implementation class MyServlet */ @WebServlet("/saveDocServlet") public class MyServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); Map<String, Object> map = new HashMap<String, Object>(); Enumeration<String> paramNames = req.getParameterNames(); // 通過迴圈將表單引數放入鍵值對對映中 while(paramNames.hasMoreElements()) { String key = paramNames.nextElement(); String value = req.getParameter(key); map.put(key, value); } // 提示:在呼叫工具類生成Word文件之前應當檢查所有欄位是否完整 // 否則Freemarker的模板殷勤在處理時可能會因為找不到值而報錯 這裡暫時忽略這個步驟了 File file = null; InputStream fin = null; ServletOutputStream out = null; try { // 呼叫工具類WordGenerator的createDoc方法生成Word文件 file = WordGenerator.createDoc(map, "resume"); fin = new FileInputStream(file); resp.setCharacterEncoding("utf-8"); resp.setContentType("application/msword"); // 設定瀏覽器以下載的方式處理該檔案預設名為resume.doc resp.addHeader("Content-Disposition", "attachment;filename=resume.doc"); out = resp.getOutputStream(); byte[] buffer = new byte[512]; // 緩衝區 int bytesToRead = -1; // 通過迴圈將讀入的Word檔案的內容輸出到瀏覽器中 while((bytesToRead = fin.read(buffer)) != -1) { out.write(buffer, 0, bytesToRead); } } finally { if(fin != null) fin.close(); if(out != null) out.close(); if(file != null) file.delete(); // 刪除臨時檔案 } } }
工具類的程式碼:
package com.lovo.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import freemarker.template.Configuration;
import freemarker.template.Template;
public class WordGenerator {
private static Configuration configuration = null;
private static Map<String, Template> allTemplates = null;
static {
configuration = new Configuration();
configuration.setDefaultEncoding("utf-8");
configuration.setClassForTemplateLoading(WordGenerator.class, "/com/lovo/ftl");
allTemplates = new HashMap<>(); // Java 7 鑽石語法
try {
allTemplates.put("resume", configuration.getTemplate("resume.ftl"));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private WordGenerator() {
throw new AssertionError();
}
public static File createDoc(Map<?, ?> dataMap, String type) {
String name = "temp" + (int) (Math.random() * 100000) + ".doc";
File f = new File(name);
Template t = allTemplates.get(type);
try {
// 這個地方不能使用FileWriter因為需要指定編碼型別否則生成的Word文件會因為有無法識別的編碼而無法開啟
Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8");
t.process(dataMap, w);
w.close();
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
return f;
}
}
JSP頁面的程式碼:
<%@ page pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
<style type="text/css">
* { font-family: "微軟雅黑"; }
.textField { border:none; border-bottom: 1px solid gray; text-align: center; }
#file { border:1px solid black; width: 80%; margin:0 auto; }
h1 input{ font-size:72px; }
td textarea { font-size: 14px; }
.key { width:125px; font-size:20px; }
</style>
</head>
<body>
<form action="saveDocServlet" method="post">
<div id="file" align="center">
<h1><input type="text" name="title" class="textField" value="我的簡歷"/></h1>
<hr/>
<table>
<tr>
<td class="key">姓名:</td>
<td><input type="text" name="name" class="textField"/></td>
<td class="key">性別:</td>
<td>
<input type="radio" name="gender" value="男" checked/>男
<input type="radio" name="gender" value="女" />女
</td>
</tr>
<tr>
<td class="key">聯絡電話:</td>
<td><input type="text" name="tel" class="textField"/></td>
<td class="key">家庭住址:</td>
<td><input type="text" name="address" class="textField"/></td>
</tr>
<tr>
<td colspan="4" class="key">個人簡介:</td>
</tr>
<tr>
<td colspan="4">
<textarea rows="10" cols="100" name="content"></textarea>
</td>
</tr>
</table>
</div>
<div align="center" style="margin-top:15px;">
<input type="submit" value="儲存Word文件" />
</div>
</form>
</body>
</html>
說明:小服務是使用註解進行配置的,因此你的伺服器需要支援Servlet 3規範,我使用的伺服器是Tomcat 7.0.52。如果你的伺服器不支援Servlet 3規範那就使用web.xml來配置你的小服務吧,其他地方沒有不同。如果你不熟悉Servlet 3規範的新特性,可以閱讀CSDN上另一篇文章,連結如下所示:
此外,如果你希望在Word文件中插入圖片,可以把Word另存為的XML檔案中代表圖片的那個很長的字串(BASE64編碼的字串)換成一個佔位符,在將要插入Word文件的圖片物件轉換成BASE64編碼的字串,用該字串替換掉佔位符就可以了,示意圖和程式碼如下所示:
將圖片轉換成BASE64字串的程式碼如下所示:
public static String getImageString(String filename) throws IOException {
InputStream in = null;
byte[] data = null;
try {
in = new FileInputStream(filename);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
throw e;
} finally {
if(in != null) in.close();
}
BASE64Encoder encoder = new BASE64Encoder();
return data != null ? encoder.encode(data) : "";
}
注意:這裡使用的BASE64Encoder類在sun.misc包下,rt.jar中有這個類,但是卻無法直接使用,需要修改訪問許可權,在Eclipse中可以這樣修改。
在專案上點右鍵選擇Properties選單項進入如下圖所示的介面:
這樣設定後就可以使用BASE64Encoder類了,在專案中呼叫getImageString方法指定要插入的圖片的完整檔名(帶路徑的檔名),該方法返回的字串就是將圖片處理成BASE64編碼後的字串。但願你按照上面的步驟一次成功!