Freemarker生成word文档

背景介绍

最近项目中提出了通过现有数据导出word文档的需求;开发语言java,运行系统为linux;导出word文档的结构比较复杂,包含单条数据展示,多条数据展示,插入图片等等,同时样式要求比较高,项目里虽然有Apache POI包,但操作word比较繁琐,同时一些功能无法实现;
网上有人通过Freemarker模板的方式来处理word文档生成,所以尝试了一下,实现了word文档的生成;
大体原理如下:
Microsoft Office遵循OpenXml标准,即,可以用xml的方式描述一个word文档,这种格式可以被各office软件提供商识别;
Freemarker可以将数据绑定到.ftl模板中,将模板存储成.doc格式即可用Microsoft Office Word 打开;


相关概念
  1. OpenXml:Ecma Office Open XML(“Open XML”)是针对字处理文档、演示文稿和电子表格的国际化开放标准,可免费供多个应用程序在多个平台上实现。Microsoft Office(2007、2003、XP、2000)、OpenOffice Novell Edition、开源项目 Gnumeric、Neo-Office 2.1 和 PalmOS (Dataviz) 已经支持 Open XML。
  2. FreeMarker:FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。

依赖包

测试项目用Maven构建,依赖如下:

		<!-- 通过模板生成word文档 -->
		<dependency>
			<groupId>org.freemarker</groupId>
			<artifactId>freemarker</artifactId>
			<version>2.3.13</version>
		</dependency>

模板

word模板:
word模板
具体模板请参考:Freemarker生成word文档
模板生成方法:
打开Word程序(建议用Microsoft Office,WPS也可以创建word文档,但兼容性不是很好,用WPS创建的模板生成之后Microsoft Office Word很多时候打不开),新建空白文档,插入需要的内容,将文件另存为“Word 2003 XML 文档(.xml)”;将文件后缀名改为“.ftl”,此时就拿到了需要的模板;
简单说明一下这个模板:

  1. <w:tbl></w:tbl>表示一个word表格;

  2. <w:tr></w:tr>表示表格里的一行;

  3. <w:tc></w:tc>表示表格里的一列;

  4. <w:p></w:p>表示一个段落;

  5. <Relationship Id="rId7" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image1.jpeg"/>表示一个图片的声明;

  6. <w:drawing></w:drawing>表示一个图片的引用;

  7. <pkg:part pkg:name="/word/media/image1.jpeg" pkg:contentType="image/jpeg" pkg:compression="store"> <pkg:binaryData>****</pkg:binaryData> </pkg:part>表示一个图片的实际内容;图片用base64字符串的方式表示,每76个字符需要换行;

Freemarker语法

这里不具体介绍Freemarker语法,仅说明用demo中用到的几个语法;

  1. ${sim.qymc}数据绑定;
  2. <#list cpls as cpl>循环列表;

代码
  1. 数据实体类
public class ExportProductBean {
	private String qymc;// 企业名
	private String hgbm;// 海关编码
	private String cksp;// 出口商品
	private String ckje;// 出口金额

	public String getQymc() {
		return qymc;
	}

	public void setQymc(String qymc) {
		this.qymc = qymc;
	}

	public String getHgbm() {
		return hgbm;
	}

	public void setHgbm(String hgbm) {
		this.hgbm = hgbm;
	}

	public String getCksp() {
		return cksp;
	}

	public void setCksp(String cksp) {
		this.cksp = cksp;
	}

	public String getCkje() {
		return ckje;
	}

	public void setCkje(String ckje) {
		this.ckje = ckje;
	}
}
  1. 测试方法
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import freemarker.cache.FileTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;

public class App {

	private static final String ENCODE = "UTF-8";
	private static final int PIC_LINE = 76;

	public static void main(String[] args) throws Exception {
		String tmplPath = "C:\\word\\";
		String tmplName = "word_ftl.ftl";
		String filePath = "C:\\word\\";
		String fileName = "word_ftl.doc";
		// 获取数据
		Map<String, Object> dataMap = getData();
		Configuration configuration = new Configuration();
		FileTemplateLoader templateLoader = new FileTemplateLoader(new File(tmplPath));
		configuration.setTemplateLoader(templateLoader);
		File outFile = new File(filePath + fileName);
		// 如果输出目标文件夹不存在,则创建
		if (!outFile.getParentFile().exists()) {
			outFile.getParentFile().mkdirs();
		}
		// 将模板和数据模型合并生成文件
		Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), ENCODE));
		Template t = configuration.getTemplate(tmplName, ENCODE);
		t.process(dataMap, out);
		out.flush();
		out.close();
	}

	private static Map<String, Object> getData() {
		Map<String, Object> dataMap = new HashMap<String, Object>();
		// 单条数据
		dataMap.put("sim", getSimple());
		// 多条数据
		dataMap.put("cpls", getComplex());
		// 图片
		dataMap.put("pic", getPic());
		return dataMap;
	}

	private static ExportProductBean getSimple() {
		ExportProductBean export = new ExportProductBean();
		export.setCkje("10万");
		export.setCksp("玻璃内胆制");
		export.setHgbm("340****333");
		export.setQymc("XX进出口有限公司");
		return export;
	}

	private static List<ExportProductBean> getComplex() {

		List<ExportProductBean> exs = new ArrayList<ExportProductBean>();

		ExportProductBean export1 = new ExportProductBean();
		export1.setCkje("10万");
		export1.setCksp("玻璃内胆制");
		export1.setHgbm("340****333");
		export1.setQymc("XX进出口有限公司");

		ExportProductBean export2 = new ExportProductBean();
		export2.setCkje("15万");
		export2.setCksp("保温瓶");
		export2.setHgbm("340****333");
		export2.setQymc("XX进出口有限公司");

		ExportProductBean export3 = new ExportProductBean();
		export3.setCkje("20万");
		export3.setCksp("其他真空容器及零件(玻璃胆除外)");
		export3.setHgbm("340****333");
		export3.setQymc("XX进出口有限公司");

		ExportProductBean export4 = new ExportProductBean();
		export4.setCkje("25万");
		export4.setCksp("烟斗及烟斗头");
		export4.setHgbm("340****333");
		export4.setQymc("XX进出口有限公司");

		exs.add(export1);
		exs.add(export2);
		exs.add(export3);
		exs.add(export4);
		return exs;
	}

	private static String getPic() {
		String pic = "/9j/4*****";// 这里省略了图片内容,具体内容请参考下载资源
		String pic_n = "";
		while (pic.length() >= PIC_LINE) {
			String lineStr = pic.substring(0, PIC_LINE);
			pic = pic.substring(PIC_LINE);
			if (pic_n.length() == 0) {
				pic_n = lineStr;
				continue;
			}
			pic_n = pic_n + "\r\n" + lineStr;
		}
		if (pic_n.length() == 0) {
			pic_n = pic;
		} else {
			if (pic.length() > 0) {
				pic_n = pic_n + "\r\n" + pic;
			}
		}
		return pic_n;
	}
}

效果

最终生成效果如下:
效果


以上是Freemarker生成word文档的全部内容,具体代码请参考:

Freemarker生成word文档

代码包描述:

  1. word/word_ftl.ftl:成word需要的模板;
  2. word/word_ftl.doc:最终效果;
  3. freemarker-word:测试代码,一个简单的Maven项目;

代码中会有些不太规范或不太优雅的写法,还请诸君不吝指证;

2018-12-14 补充:
工作中经常出现一个问题:本地(windows系统)生成的文档可以打开,但服务器上(linux系统)生成的同样的报告无法打开,仔细对比研究后发现是windows和linux中换行不同引起的

Windows下换行符号是“\r\n”,而linux下是“\n”没有”\r”

解决方法:
代码中强制将“\n”替换成“\r\n”;

2020-07-31 补充:
这种方式生成的word有诸多问题:
1.体积过大,由于文档是xml格式的,一旦内容比较多的时候,文档体积会很大,打开过程会变的很慢;
2.无法用程序插入目录;
解决方法:
.NET中用C#操作word相对比较简单,也更灵活;我们的处理方式是写一个.NET的WebService,在这个WebService里加载这个word文档,另存成普通的word格式,并添加自动生成的目录;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值