Web項目中生成Word文檔的操作屢見不鮮,基於Java的解決方案也是很多的,包括使用Jacob、Apache POI、Java2Word、iText等各種方式,其實在從Office 2003開始,就可以將Office文檔轉換成XML文件,這樣只要將需要填入的內容放上${}占位符,就可以使用像Freemarker這樣的模板引擎將出現占位符的地方替換成真實數據,這種方式較之其他的方案要更為簡單。

下面舉一個簡單的例子,比如在Web頁面中填寫個人簡歷,然后點擊保存下載到本地,效果圖如下所示。

打開下載的Word文件

首先在Eclipse Java EE版中新建一個Dynamic Web Project,項目結構如下圖所示

需要向項目中加入freemarker的JAR文件,可以通過下面的鏈接獲得Freemarker的最新版本:
http://freemarker.org/freemarkerdownload.html

模板文件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上另一篇文章,鏈接如下所示:
http://blog.csdn.net/zhongweijian/article/details/8279650
此外,如果你希望在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編碼后的字符串。但願你按照上面的步驟一次成功!
