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的最新版本:

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编码后的字符串。但愿你按照上面的步骤一次成功!吐舌头

  • 52
    点赞
  • 123
    收藏
    觉得还不错? 一键收藏
  • 29
    评论
Web应用,有时需要按照固定的模板将数据导出到Word,如流程审批单,在流程处理完成后将处理过程按照流程单的要求导出,有时程序需要实现生成标准Word文档,要求能够打印,并且保持页面样式不变,常见的方案有POI、iText、JACOB、JSP几种方式,POI读取Word文档比较适合、对于生成文档样式比较难控制,iText操作Excel还可以,对Word的操作功能有限,JACOB操作Word实现复杂,并且无法将服务部署到Linux平台,要求安装office,对于实现固定格式的报表实现困难,对于JSP直接输出方式样式控制难。 Word从2003开始支持XML格式,用XML+Freemarder还做就很简单了,大致的思路是先用office2003或者2007编辑好 word的样式,然后另存为xml,将xml翻译为FreeMarker模板,最后用java来解析FreeMarker模板并输出Doc。经测试这样方式生成word文档完全符合office标准,样式、内容控制非常便利,打印也不会变形,生成的文档和office编辑文档完全一样。具体实现过程如下: 1、 首先用office【版本要2003以上,以下的不支持xml格式】编辑文档的样式,将需要动态填充的内容使用Freemarker标签替换:Word文档样式如下: 2、 将Word文档另存为XML格式,将后缀名“xml”修改为“ftl” 3、 使用Freemarker填充内容,代码如下: [java] view plaincopyprint? 1. package com.test.freemarker.report; 2. 3. 4. 5. import java.io.BufferedWriter; 6. 7. import java.io.File; 8. 9. import java.io.FileOutputStream; 10. 11. import java.io.IOException; 12. 13. import java.io.OutputStreamWriter; 14. 15. import java.io.Writer; 16. 17. import java.util.HashMap; 18. 19. import java.util.Map; 20. 21. 22. 23. import freemarker.template.Configuration; 24. 25. import freemarker.template.Template; 26. 27. import freemarker.template.TemplateException; 28. 29. 30. 31. public class DocumentHandler { 32. 33. private Configuration configuration = null; 34. 35. 36. 37. public DocumentHandler() { 38. 39. configuration = new Configuration(); 40. 41. configuration.setDefaultEncoding("utf-8"); 42. 43. } 44. 45. 46. 47. public void createDoc() { 48. 49. // 要填入模本的数据文件 50. 51. Map dataMap = new HashMap(); 52. 53. getData(dataMap); 54. 55. // 设置模本装置方法和路径,FreeMarker支持多种模板装载方法。可以重servlet,classpath,数据库装载, 56. 57. // 这里我们的模板是放在com.havenliu.document.template包下面 58. 59.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值