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的制作了,代碼如下所示:
 
小服務的代碼:
 
 
   [java] view plain copy print ?   
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;????????@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);??????????}????????????????????????????????????File?file?=?null;??????????InputStream?fin?=?null;??????????ServletOutputStream?out?=?null;??????????try?{????????????????????????????file?=?WordGenerator.createDoc(map,?"resume");??????????????fin?=?new?FileInputStream(file);????????????????????????????resp.setCharacterEncoding("utf-8");??????????????resp.setContentType("application/msword");????????????????????????????resp.addHeader("Content-Disposition",?"attachment;filename=resume.doc");????????????????????????????out?=?resp.getOutputStream();??????????????byte[]?buffer?=?new?byte[512];????????????????int?bytesToRead?=?-1;????????????????????????????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.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;@Overrideprotected 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.docresp.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();	// 刪除臨時文件}}}
 
 
 
工具類的代碼:
 
 
   [java] view plain copy print ?   
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<>();?????????????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?{????????????????????????????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;??????}????}??    
 
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頁面的代碼:
 
 
   [html] view plain copy print ?   
<%@?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>??    
 
<%@ 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字符串的代碼如下所示:
 
 
   [java] view plain copy print ?   
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)?:?"";??????}??    
 
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編碼后的字符串。但愿你按照上面的步驟一次成功!
                            總結
                            
                                以上是生活随笔為你收集整理的Java Web项目中使用Freemarker生成Word文档的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                            
                                如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。