SpringBoot2.0java生成word文档(项目源码freeMarker实现)
1.需求说明
word中含有40几个需要填充的数据项点,以及10几个表格的数据需要生成
部分截图如下
2.技术选型
java生成word六种方式
https://www.cnblogs.com/zhongshiqiang/p/5764857.html
因为两点选择使用freeMarker的方式,1.linux环境 2.复杂的格式和表样
3.功能点
1.word中数据项点的自动参数匹配,因为数据项太多,如果人为操作容易出错,而且复杂耗时
2.word中表格项的特殊处理(freemaker中list标签的使用),需要在xml文档中table数据项的前后加上list标签
备注:1和2两点功能使用dom4j技术进行操作
https://blog.****.net/xiewenfeng520/article/details/88362679
3.freemaker通过${xx}的方式,将数据填充到word中
4.源代码
依赖引入
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
模板自动生成工具 XmlModelUtils.java
1.word中数据项点的自动参数匹配,将word中的**替换为对应的${xx},方法名 replaceDocumentBychr
2.word中表格前后添加freemaker <#list>标签,方法名 addListDocumentForTable
package com.huajie.utils.word;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
public class XmlModelUtils {
private File inputXml;
private static int i =0;
private static int tableIndex =0;
private static List listParamCode = new ArrayList<String>();
private static List listTableName = new ArrayList<String>();
public XmlModelUtils(File inputXml) {
this.inputXml = inputXml;
}
public static void main(String[] argv) throws DocumentException, IOException {
for (int j = 0; j <102 ; j++) {
listParamCode.add("leadergroup_"+j+"");
}
for (int j = 0; j <14 ; j++) {
listTableName.add("list"+j+"");
}
XmlModelUtils dom4jParser = new XmlModelUtils(new File("D:\\qrcodetmp\\freemaker.xml"));
Document document = dom4jParser.getDocument();
//将所有的**替换为对应的值
dom4jParser.replaceDocumentBychr(document.getRootElement());
//在所有的table前后增加freemaker list标签
dom4jParser.addListDocumentForTable(document);
XMLWriter output = new XMLWriter(new FileWriter(new File(
"D:\\qrcodetmp\\students-modified.ftl")));
output.setEscapeText(false);
output.write(document);
output.close();
}
private void addListDocumentForTable(Document document) {
document.getRootElement().addNamespace("w","http://schemas.openxmlformats.org/wordprocessingml/2006/main");
List nodes = document.getRootElement().selectNodes("//w:tbl");
Iterator iter = nodes.iterator();
while (iter.hasNext()) {
tableIndex++;
Element element = (Element) iter.next();
Iterator iterator = element.elementIterator("tr");
while (iterator.hasNext()) {
Element nameElement = (Element) iterator.next();
if(!iterator.hasNext()){
Element currentElement = nameElement;
Element pNode = nameElement.getParent();
pNode.remove(nameElement);
Element listElementHead = pNode.addElement("w:r");
listElementHead.setText("<#list "+listTableName.get(tableIndex-1)+" as "+listTableName.get(tableIndex-1)+" > ");
// listElementHead.setText("<#list listLeadergroup as listLeadergroup>");
pNode.add(currentElement);
Element listElementTail = pNode.addElement("w:r");
listElementTail.setText("</#list>");
}
}
}
document.getRootElement().remove(new Namespace("w","http://schemas.openxmlformats.org/wordprocessingml/2006/main"));
}
public Document getDocument() {
SAXReader saxReader = new SAXReader();
Document document = null;
try {
document = saxReader.read(inputXml);
} catch (Exception e) {
e.printStackTrace();
}
return document;
}
public void replaceDocumentBychr(Element element) {
// 枚举根节点下所有子节点
for (Iterator ie = element.elementIterator(); ie.hasNext();) {
Element elementa = (Element) ie.next();
// 枚举当前节点下所有子节点
for (Iterator ieson = elementa.elementIterator(); ieson.hasNext();) {
Element elementSon = (Element) ieson.next();
if(elementSon.getText().contains("*")){
// System.out.println(elementSon.getName() + ":"+ elementSon.getText());
String textNew = replaceText(elementSon.getText());
elementSon.setText(textNew);
}
}
replaceDocumentBychr(elementa);
}
}
public String replaceText(String text){
Integer cnt = getCountByChr(text,"**");
Integer index = text.indexOf("**");
i++;
if(cnt>1){
text = text.substring(0,index+2).replace("**","${"+listParamCode.get(i-1)+"}")+text.substring(index+2);
return replaceText(text);
}else{
text = text.replace("**","${"+listParamCode.get(i-1)+"}");
}
return text;
}
private Integer getCountByChr(String text, String chr) {
int count = 0;
int index;
//先查,赋值,判断
while((index=text.indexOf(chr))!=-1){
count++;
text = text.substring(index + chr.length());
}
return count;
}
}
替换完的xml文档如下
数据项
表格
模板制作完成之后,只需要使用freemaker填充数据即可
以下代码参考网上的一些博客,但是有一个坑要做说明
网上代码都是用的上面一行来处理freemaker 模板文件路径,在springboot的版本中不太适用,模板文件需要经常替换。本文修改为服务器路径,linux可以替换路径地址
代码中用到lombok需要自行下载
package com.huajie.utils.word;
import lombok.Data;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.*;
@Data
public class WordAction {
private String filePath; //文件路径
private String fileName; //文件名称
private String fileOnlyName; //文件唯一名称
public String createWord() {
/** 用于组装word页面需要的数据 */
Map<String, Object> dataMap = new HashMap<String, Object>();
/** 组装数据 */
for (int j = 0; j <103 ; j++) {
dataMap.put("leadergroup_"+j+"",j);
}
List<Map<String, Object>> listLeadergroup = new ArrayList<Map<String, Object>>();
for (int i = 1; i <= 10; i++) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", "标题" + i);
listLeadergroup.add(map);
}
for (int j = 0; j <15 ; j++) {
dataMap.put("list"+j+"",listLeadergroup);
}
/** 文件名称,唯一字符串 */
Random r = new Random();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd");
StringBuffer sb = new StringBuffer();
sb.append(sdf1.format(new Date()));
sb.append("_");
sb.append(r.nextInt(100));
//文件路径
filePath = "D:/qrcodetmp/";
//文件唯一名称
fileOnlyName = "用freemarker生成Word文档_" + sb + ".doc";
//文件名称
fileName = "用freemarker生成Word文档.doc";
/** 生成word */
WordUtil.createWord(dataMap, "students-modified.ftl", filePath, fileOnlyName);
return "createWordSuccess";
}
/**
*
* 下载生成的word文档
*/
public String dowloadWord() {
/** 先判断文件是否已生成 */
try {
//解决中文乱码
filePath = URLDecoder.decode(filePath, "UTF-8");
fileOnlyName = URLDecoder.decode(fileOnlyName, "UTF-8");
fileName = URLDecoder.decode(fileName, "UTF-8");
//如果文件不存在,则会跳入异常,然后可以进行异常处理
new FileInputStream(filePath + File.separator + fileOnlyName);
} catch (Exception e) {
e.printStackTrace();
return "error";
}
return "dowloadWord";
}
/**
* 返回最终生成的word文档 文件流
* 下载生成的word文档
*/
public InputStream getWordFile() {
try {
//解决中文乱码
fileName = URLDecoder.decode(fileName, "UTF-8");
/** 返回最终生成的word文件流 */
return new FileInputStream(filePath + File.separator + fileOnlyName);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) throws Exception {
new WordAction().createWord();
}
}
WordUtil
package com.huajie.utils.word;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.*;
import java.util.Map;
public class WordUtil {
/**
* 生成word文件
* @param dataMap word中需要展示的动态数据,用map集合来保存
* @param templateName word模板名称,例如:test.ftl
* @param filePath 文件生成的目标路径,例如:D:/wordFile/
* @param fileName 生成的文件名称,例如:test.doc
*/
@SuppressWarnings("unchecked")
public static void createWord(Map dataMap,String templateName,String filePath,String fileName){
try {
//创建配置实例
Configuration configuration = new Configuration();
//设置编码
configuration.setDefaultEncoding("UTF-8");
//ftl模板文件
// configuration.setClassForTemplateLoading(WordUtil.class,"/");
configuration.setDirectoryForTemplateLoading(new File("D:/qrcodetmp/"));
//获取模板
Template template = configuration.getTemplate(templateName);
//输出文件
File outFile = new File(filePath+File.separator+fileName);
//如果输出目标文件夹不存在,则创建
if (!outFile.getParentFile().exists()){
outFile.getParentFile().mkdirs();
}
//将模板和数据模型合并生成文件
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8"));
//生成文件
template.process(dataMap, out);
//关闭流
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}