java导出2007版word(docx格式)freemarker + xml 实现
http://blog.csdn.net/yigehui12/article/details/52840121
Freemarker+xml生成docx
原理概述:word从2003版就支持xml格式,而freemarker是java封装的模板工具,两者结合也就是在xml中需要动态生成的部分调用freemarker的指令(类似于EL表达式),来生成我们需要的数据,再用流输出文件,就达到了写word的效果。
生成word的基本流程图如下:
1. 生成docx模板和xml模板
生成docx模板
按照项目需要生成固定格式的docx格式的模板。
为方便测试做了个简单的例子,docx模板的内容如下图:
生成xml模板
从docx模板中取出word/document.xml,由于docx属于zip格式,可以用winrar打开,如图:
除word文件夹外其它文件为基本配置文件,取出word/document.xml(存放word文件的文本内容)如下图:
需要把document.xml解压出来,修改里面需要从数据库导出的数据用freemarker的指令代替,例如${test} 同时可以用遍历函数
替换完成后相当于生成了xml模板
生成的Xml模板:
- <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
- <w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 wp14">
- <w:body>
- <w:p w:rsidR="009175C2" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
- <w:pPr>
- <w:pStyle w:val="1"/>
- <w:jc w:val="center"/>
- <w:rPr>
- <w:rFonts w:hint="eastAsia"/>
- </w:rPr>
- </w:pPr>
- <w:bookmarkStart w:id="0" w:name="_GoBack"/>
- <w:bookmarkEnd w:id="0"/>
- <w:r>
- <w:rPr>
- <w:rFonts w:hint="eastAsia"/>
- </w:rPr>
- <w:t>
- 这是一个测试Word</w:t>
- </w:r>
- </w:p>
- <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
- <w:pPr>
- <w:rPr>
- <w:rFonts w:hint="eastAsia"/>
- </w:rPr>
- </w:pPr>
- </w:p>
- <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
- <w:pPr>
- <w:rPr>
- <w:rFonts w:hint="eastAsia"/>
- </w:rPr>
- </w:pPr>
- </w:p>
- <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
- <w:pPr>
- <w:pStyle w:val="a3"/>
- <w:numPr>
- <w:ilvl w:val="0"/>
- <w:numId w:val="1"/>
- </w:numPr>
- <w:ind w:firstLineChars="0"/>
- <w:rPr>
- <w:rFonts w:hint="eastAsia"/>
- </w:rPr>
- </w:pPr>
- <w:r>
- <w:rPr>
- <w:rFonts w:hint="eastAsia"/>
- </w:rPr>
- <w:t>
- ${test}</w:t>
- </w:r>
- <w:r>
- <w:rPr>
- <w:rFonts w:hint="eastAsia"/>
- </w:rPr>
- <w:t xml:space="preserve">
- </w:t>
- </w:r>
- </w:p>
- <#list students as s>
- <w:p w:rsidR="005B5FB3" w:rsidRDefault="005B5FB3" w:rsidP="005B5FB3">
- <w:pPr>
- <w:pStyle w:val="a3"/>
- <w:numPr>
- <w:ilvl w:val="0"/>
- <w:numId w:val="1"/>
- </w:numPr>
- <w:ind w:firstLineChars="0"/>
- <w:rPr>
- <w:rFonts w:hint="eastAsia"/>
- </w:rPr>
- </w:pPr>
- <w:r>
- <w:rPr>
- <w:rFonts w:hint="eastAsia"/>
- </w:rPr>
- <w:t>
- ${s}
- </w:t>
- </w:r>
- </w:p>
- </#list>
- <w:sectPr w:rsidR="005B5FB3" w:rsidRPr="005B5FB3">
- <w:pgSz w:w="11906" w:h="16838"/>
- <w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="851" w:footer="992" w:gutter="0"/>
- <w:cols w:space="425"/>
- <w:docGrid w:type="lines" w:linePitch="312"/>
- </w:sectPr>
- </w:body>
- </w:document>
2. 利用freemarker填充数据并生成word文件
这里把数据库中的数据放到map中,把map和xml模板交给freemarker来生成(填充完数据)的xml具体实现方法如下:
- package com.hannet.yigehui;
- 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;
- import freemarker.template.TemplateException;
- public class XmlToExcel {
- private static XmlToExcel tplm = null;
- private Configuration cfg = null;
- private XmlToExcel() {
- cfg = new Configuration();
- try {
- //注册tmlplate的load路径
- cfg.setClassForTemplateLoading(this.getClass(), "/template/");
- } catch (Exception e) {
- }
- }
- private static Template getTemplate(String name) throws IOException {
- if(tplm == null) {
- tplm = new XmlToExcel();
- }
- return tplm.cfg.getTemplate(name);
- }
- /**
- *
- * @param templatefile 模板文件
- * @param param 需要填充的内容
- * @param out 填充完成输出的文件
- * @throws IOException
- * @throws TemplateException
- */
- public static void process(String templatefile, Map param ,Writer out) throws IOException, TemplateException{
- //获取模板
- Template template=XmlToExcel.getTemplate(templatefile);
- template.setOutputEncoding("UTF-8");
- //合并数据
- template.process(param, out);
- if(out!=null){
- out.close();
- }
- }
- }
利用freemarker生成数据
调用freemarker中的process方法(上图中的方法)来填充数据。
例(用1中生成的xml为模板填充数据):
//xml的模板路径 template/test.xml
String xmlTemplate = "test.xml";
//填充完数据的临时xml
String xmlTemp = "d:\\temp.xml";
Writer w = new FileWriter(new File(xmlTemp));
//1.需要动态传入的数据
Map<String,Object> p = new HashMap<String,Object>();
List<String> students = new ArrayList<String>();
students.add("张三");
students.add("李四");
students.add("王二");
p.put("test", "测试一下是否成功");
p.put("students", students);
//2.把map中的数据动态由freemarker传给xml
XmlToExcel.process(xmlTemplate, p, w);
注:下文中会给出源码这里只是方便理解。
导出word文件
利用java的zipFile 和 ZipOutputStream 及zipFile.getInputStream() 来根据docx模板导出 (需要把word/document.xml文件替换成(填充完数据)的xml)具体操作流程如下:
- package com.hannet.yigehui;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.FileWriter;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.Writer;
- import java.net.URISyntaxException;
- import java.util.ArrayList;
- import java.util.Enumeration;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.zip.ZipEntry;
- import java.util.zip.ZipException;
- import java.util.zip.ZipFile;
- import java.util.zip.ZipOutputStream;
- import freemarker.template.TemplateException;
- /**
- * 其实docx属于zip的一种,这里只需要操作word/document.xml中的数据,其他的数据不用动
- * @author yigehui
- *
- */
- public class XmlToDocx {
- /**
- *
- * @param documentFile 动态生成数据的docunment.xml文件
- * @param docxTemplate docx的模板
- * @param toFileName 需要导出的文件路径
- * @throws ZipException
- * @throws IOException
- */
- public void outDocx(File documentFile,String docxTemplate,String toFilePath) throws ZipException, IOException {
- try {
- String fileName = XmlToDocx.class.getClassLoader().getResource("").toURI().getPath()+docxTemplate;
- File docxFile = new File(fileName);
- ZipFile zipFile = new ZipFile(docxFile);
- Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();
- ZipOutputStream zipout = new ZipOutputStream(new FileOutputStream(toFilePath));
- int len=-1;
- byte[] buffer=new byte[1024];
- while(zipEntrys.hasMoreElements()) {
- ZipEntry next = zipEntrys.nextElement();
- InputStream is = zipFile.getInputStream(next);
- //把输入流的文件传到输出流中 如果是word/document.xml由我们输入
- zipout.putNextEntry(new ZipEntry(next.toString()));
- if("word/document.xml".equals(next.toString())){
- //InputStream in = new FileInputStream(new File(XmlToDocx.class.getClassLoader().getResource("").toURI().getPath()+"template/test.xml"));
- InputStream in = new FileInputStream(documentFile);
- while((len = in.read(buffer))!=-1){
- zipout.write(buffer,0,len);
- }
- in.close();
- }else {
- while((len = is.read(buffer))!=-1){
- zipout.write(buffer,0,len);
- }
- is.close();
- }
- }
- zipout.close();
- } catch (URISyntaxException e) {
- e.printStackTrace();
- }catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- }
- }
这里输出的文件即为完整的docx格式的word文件
测试调用的代码:
- public static void main(String[] args) throws IOException, TemplateException {
- //xml的模板路径*/*
- String xmlTemplate = "test.xml";
- //设置docx的模板路径 和文件名
- String docxTemplate = "template/test.docx";
- String toFilePath = "d:\\test.docx";
- //填充完数据的临时xml
- String xmlTemp = "d:\\temp.xml";
- Writer w = new FileWriter(new File(xmlTemp));
- //1.需要动态传入的数据
- Map<String,Object> p = new HashMap<String,Object>();
- List<String> students = new ArrayList<String>();
- students.add("张三");
- students.add("李四");
- students.add("王二");
- p.put("test", "测试一下是否成功");
- p.put("students", students);
- //2.把map中的数据动态由freemarker传给xml
- XmlToExcel.process(xmlTemplate, p, w);
- //3.把填充完成的xml写入到docx中
- XmlToDocx xtd = new XmlToDocx();
- xtd.outDocx(new File(xmlTemp), docxTemplate, toFilePath);
- }
调用成功后生成的test.docx如下图:
注:
测试项目的目录结构:
需要引入的包:
链接:http://pan.baidu.com/s/1eS7JHia 密码:tlec
java导出2007版word(docx格式)freemarker + xml 实现的更多相关文章
- Java 使用模板生成 Word 文件---基于 Freemarker 模板框架
Java项目引入 Freemarker 插件自行完成. 步骤如下: .编写 Word 模板,并将模板中要用代码动态生成数据用 Freemarker 变量取代,即${变量名},如${username}: ...
- java 导出数据为word文档(保持模板格式)
导出数据到具体的word文档里面,word有一定的格式,需要保持不变 这里使用freemarker来实现: ①:设计好word文档格式,需要用数据填充的地方用便于识别的长字符串替换 如 aaaaa ...
- NPOI 2.0导出word(docx格式)
大名鼎鼎的NPOI用来导出EXCEL的文章园子里面有很多,可是用来导出WORD文档的文章大都含糊不清,最近刚好完成一个导出WORD文档的需求,在此分享下. NPOI里面认为word文档的最基本的结构是 ...
- java用freemarker导出数据到word(含多图片)
一.制作word模版 新建word文档,按照需要设置好字体等各种格式:这里为了显得整齐使用了无边框的表格. 将word文档另存为xml文件(注意不是word xml文档,我吃了这家伙的大亏了) 然后用 ...
- java导出生成word(类似简历导出)
参考帖子: http://www.cnblogs.com/lcngu/p/5247179.html http://www.cnblogs.com/splvxh/archive/2013/03/15/2 ...
- Atitit.excel导出 功能解决方案 php java C#.net版总集合.doc
Atitit.excel导出 功能解决方案 php java C#.net版总集合.docx 1.1. Excel的保存格式office2003 office2007/2010格式1 1.2. 类库选 ...
- java导出生成word
最近做的项目,需要将一些信息导出到word中.在网上找了好多解决方案,现在将这几天的总结分享一下. 目前来看,java导出word大致有6种解决方案: 1:Jacob是Java-COM Bridge的 ...
- java导出word的6种方式(复制来的文章)
来自: http://www.cnblogs.com/lcngu/p/5247179.html 最近做的项目,需要将一些信息导出到word中.在网上找了好多解决方案,现在将这几天的总结分享一下. 目前 ...
- FreeMarker + xml 导出word
转载自:http://hongqiang.iteye.com/blog/1632998 首先介绍几种java导出word方案 1.Jacob是Java-COM Bridge的缩写,它在Java与微软的 ...
随机推荐
- Hillstone防火墙sslvpn配置与使用
1.山石的sslvpn称为Secure Connect VPN,即scvpn. 2.WEB界面登陆防火墙,“用户”,“AAA服务器”,新建用户: 3.定义源IP池 即用户通过sslvpn拨号成功后获取 ...
- Java中关于quartz定时任务时间设置
每天凌晨2点 0 0 2 * * ?和每天隔一小时 0 * */1 * * ? 例1:每隔5秒执行一次:*/5 * * * * ? 例2:每隔5分执行一次:0 */5 * * * ?在26分.29分. ...
- CentOS-07安装Redis学习笔记
CentOS-07安装Redis 下载 http://download.redis.io/releases/redis-3.0.0.tar.gz 安装第一步:将下载的Redis源码包上传大奥Linux ...
- ARM指令集详解
一.跳转指令 B: 跳转指令 BL: 带返回的跳转指令 BLX: 带返回和状态切换的跳转指令 BX: 带状态切换的跳转指令 二.数据处理指令 1.MOV:数据传送指令 MOV{条件}{S} 目的 ...
- awk命令小结
先在此至敬朱双印老师,博客写得很详细:http://www.zsythink.net/archives/tag/awk/ 这是朱双印老师关于awk博客的链接,强力推荐给大家 AWK一般在网上说是一 ...
- 进程&线程(转)
(摘自:http://www.cnblogs.com/CareySon/archive/2012/05/04/ProcessAndThread.html) 在传统的操作系统中,进程拥有独立的内存地址空 ...
- Python之路(第三十三篇) 网络编程:socketserver深度解析
一.socketserver 模块介绍 socketserver是标准库中的一个高级模块,用于网络客户端与服务器的实现.(version = "0.4") 在python2中写作S ...
- java数据结构至对称矩阵压缩存储
刚刚刷java选择题,遇到的对称矩阵压缩存储问题,我们知道对称矩阵是aij=aji的矩阵,压缩存储可以采用一维数组和二维数组存储. 此处只讨论一维数组存储的形式,设数组下标从0开始,对称矩阵为n维矩阵 ...
- brace源码改造实现跨服务器监控-zjs
1.从GitHub上下载源码,本地编译,有部分代码编译报错,如下图: 百度搜索import sun.jvmstat.monitor.需要导入什么jar包:导入jdk/lib/tools.jar.
- vb编程中的is是什么意思??
在select case 语句中可以使用关系运算符大于>小于<等于=等关系运算符,需要用关键字IS和TO.用个例子来说明:Private Sub Command1_Click()Dim a ...