您的位置:首页 > 编程语言 > Java开发

java导出2007版word(docx格式)freemarker + xml 实现

2017-11-27 15:38 639 查看
/Uploads/Images/Content/201711/237e9ea84b730e1a98a1e0621e31f47f

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模板:

[html] view plain copy

<?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具体实现方法如下:

[java] view plain copy

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)具体操作流程如下:

[java] view plain copy

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文件

测试调用的代码:

[java] view plain copy

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐