1. 程式人生 > >Java Web專案中使用Freemarker生成Word文件

Java Web專案中使用Freemarker生成Word文件

Web專案中生成Word文件的操作屢見不鮮,基於Java的解決方案也是很多的,包括使用JacobApache 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編碼後的字串。但願你按照上面的步驟一次成功!吐舌頭