FreeMarker生成复杂word(包含图片,表格)

1 篇文章 0 订阅
1 篇文章 0 订阅

Web项目中生成Word文档的操作屡见不鲜,基于Java的解决方案也是很多的,包括使用JacobApache POIJava2WordiText等各种方式,其实在从Office 2003开始,就可以将Office文档转换成XML文件,这样只要将需要填入的内容放上${}占位符,就可以使用像FreeMarker这样的模板引擎将出现占位符的地方替换成真实数据,这种方式较之其他的方案要更为简单。这个功能就是由XML+FreeMarker来实现的,Word从2003开始支持XML格式,大致的步骤:用office2003或者以上的版本编辑好word的样式,然后另存为xml,将xml翻译为FreeMarker模板,最后用java来解析FreeMarker模板并输出Doc。使用Freemarker其实就只准备模板和数据

制作resume.ftl模板

首先用office【版本要2003以上,以下的不支持xml格式】编辑文档的样式,将需要动态填充的内容使用Freemarker标签替换,将Word文档另存为XML格式(注意是另存为,不是直接改扩展名) 建议用Editplus、Notepad++、Sublime等工具打开查看一下,因为有的时候你写的占位符可能会被拆开,这样Freemarker就无法处理了。



打开XML文件看看,如果刚才你写的${qno}、${title}等被xml文件给拆散了,修改一下XML文件就OK了


修改过后另存为resume.ftl模板文件,如下所示:


接下来修改图片,搜索w:binData 或者 png可以快速定位图片的位置,图片已经是0-F的字符串了,换成一个占位符,在将要插入Word文档的图片对象转换成BASE64编码的字符串,用该字符串替换掉占位符就可以了,示意图如下所示:

接下来就可以编码啦:

服务的代码:

@RequestMapping(value = "/demo/word", method = RequestMethod.POST)
	public void word(@RequestParam(value = "image", required = false) MultipartFile image, ModelMap model, HttpServletRequest req, HttpServletResponse resp) throws IOException {
		req.setCharacterEncoding("utf-8");
		Map<String, Object> map = null;
		Enumeration<String> paramNames = req.getParameterNames();
		// 通过循环将表单参数放入键值对映射中  
		/*while (paramNames.hasMoreElements()) {
			String key = paramNames.nextElement();
			String value = req.getParameter(key);
			map.put(key, value);
		}*/
		InputStream in = image.getInputStream();
		String reportImage = WordGenerator.getImageString(in);
		String jsonStr = "{\"exam\": [{\"ecnt\": 4,\"eid\": \"25\",\"options\": [{\"choice\": \"3\",\"option\": \"男\",\"rowratio\": \"75.00%\"}, {\"choice\": \"1\",\"option\": \"女\",\"rowratio\": \"25.00%\"}],\"qno\": \"3\",\"title\": \"你的性别是\",\"types\": \"单选题\"}]}";
		JSONObject jsonObject = JSONObject.parseObject(jsonStr);
		List exams = jsonObject.getJSONArray("exam");
		List<Map<String, Object>> arrayList = new ArrayList<Map<String, Object>>();
		for (int i = 0; i < exams.size(); i++) {
			map = new HashMap<String, Object>();
			JSONObject exam = (JSONObject) exams.get(i);
			map.put("ecnt", exam.get("ecnt"));
			map.put("eid", exam.get("eid"));
			map.put("qno", exam.get("qno"));
			map.put("title", exam.get("title"));
			map.put("types", exam.get("types"));
			map.put("reportImage", reportImage);
			List<Map<String, String>> options = (List) exam.getJSONArray("options");
			List<Map<String, Object>> optionList = new ArrayList<Map<String, Object>>();
			for (Map tempMap : options) {
				Map optionMap = new HashMap<String, Object>();
				optionMap.put("choice", tempMap.get("choice"));
				optionMap.put("option", tempMap.get("option"));
				optionMap.put("rowratio", tempMap.get("rowratio"));
				optionList.add(optionMap);
			}
			map.put("options", optionList);
			arrayList.add(map);
		}

		// 提示:在调用工具类生成Word文档之前应当检查所有字段是否完整  
		// 否则Freemarker的模板殷勤在处理时可能会因为找不到值而报错 这里暂时忽略这个步骤了  
		File file = null;
		InputStream fin = null;
		OutputStream out = null;
		try {
			// 调用工具类WordGenerator的createDoc方法生成Word文档  
			Map root = new HashMap<String, Object>();
			root.put("questTitle", "测试word导出");
			root.put("exams", arrayList);
			file = WordGenerator.createDoc(root, "resume");
			fin = new FileInputStream(file);
			resp.setCharacterEncoding("utf-8");
			resp.setContentType("application/ms-word;charset=utf-8");
			// 设置浏览器以下载的方式处理该文件默认名为resume.doc  
			resp.addHeader("Content-Disposition", "attachment;filename=resume.doc");

			out = resp.getOutputStream();
			// 写出流信息
			/*	while ((len = fs.read()) != -1) {
					os.write(len);
				}*/
			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 cn.comm.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import freemarker.template.Configuration;
import freemarker.template.Template;
import sun.misc.BASE64Encoder;

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, "/cn/comm/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.flush();
			w.close();
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new RuntimeException(ex);
		}
		return f;
	}

	//将图片转换成BASE64字符串
	public static String getImageString(InputStream in) 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) : "";
	}

}
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="/mybase/demo/word" method="post" enctype="multipart/form-data">  
    <div id="file" align="center">  
        <h1><input type="text" name="title" class="textField" value="测试word导出"/></h1>  
        <hr/>  
        <table>  
            
            <tr>  
                <td colspan="4">  
                    <input type="file" name="image" /> 
                </td>  
            </tr>  
        </table>  
    </div>  
    <div align="center" style="margin-top:15px;">  
        <input type="submit" value="保存Word文档" />  
    </div>  
    </form>  
</body>  
</html>  
注意:这里使用的BASE64Encoder类在sun.misc包下,rt.jar中有这个类,但是却无法直接使用,需要修改访问权限,在Eclipse中可以这样修改。

在项目上点右键选择Properties菜单项进入如下图所示的界面:


这样设置后就可以使用BASE64Encoder类了,在项目中调用getImageString 方法指定要插入的图片的完整文件名(带路径的文件名),该方法返回的字符串就是将图片处理成BASE64编码后的字符串。

到这里就可以导出一个复杂的带表格,图片的wors文档啦。导出来就是这样的,如下图:





  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个使用 FreeMarker 生成 Word 文档的示例。 首先需要在 pom.xml 中添加以下依赖: ```xml <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> ``` 然后可以创建一个 FreeMarker 模板文件,例如 `template.ftl`: ```html <html> <head> <style> table { border-collapse: collapse; width: 100%; } th, td { border: 1px solid black; text-align: left; padding: 8px; } th { background-color: #ddd; } </style> </head> <body> <h1>${title}</h1> <table> <tr> <th>Name</th> <th>Age</th> <th>Email</th> </tr> <#list users as user> <tr> <td>${user.name}</td> <td>${user.age}</td> <td>${user.email}</td> </tr> </#list> </table> </body> </html> ``` 这个模板包含了一个表格,其中列出了用户的姓名、年龄和电子邮件地址。 然后可以使用以下代码读取模板并将数据填充到模板中: ```java import freemarker.template.Configuration; import freemarker.template.Template; import org.apache.poi.xwpf.usermodel.*; import java.io.*; import java.util.*; public class WordGenerator { public static void generate(Map<String, Object> data, OutputStream outputStream) throws Exception { // 创建 FreeMarker 配置 Configuration cfg = new Configuration(Configuration.VERSION_2_3_30); cfg.setClassForTemplateLoading(WordGenerator.class, "/"); cfg.setDefaultEncoding("UTF-8"); // 加载模板 Template template = cfg.getTemplate("template.ftl"); // 创建 Word 文档 XWPFDocument document = new XWPFDocument(); // 将模板填充到 Word 文档中 StringWriter writer = new StringWriter(); template.process(data, writer); String html = writer.toString(); InputStream inputStream = new ByteArrayInputStream(html.getBytes()); XWPFHtmlConverter.getInstance().convert(inputStream, document); // 保存 Word 文档 document.write(outputStream); outputStream.close(); } public static void main(String[] args) throws Exception { // 准备数据 Map<String, Object> data = new HashMap<>(); data.put("title", "User List"); List<Map<String, Object>> users = new ArrayList<>(); users.add(createUser("Alice", 25, "alice@example.com")); users.add(createUser("Bob", 30, "bob@example.com")); users.add(createUser("Charlie", 35, "charlie@example.com")); data.put("users", users); // 生成 Word 文档 generate(data, new FileOutputStream("output.docx")); } private static Map<String, Object> createUser(String name, int age, String email) { Map<String, Object> user = new HashMap<>(); user.put("name", name); user.put("age", age); user.put("email", email); return user; } } ``` 这个示例将生成一个名为 `output.docx` 的 Word 文档,其中包含了填充后的表格
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值