SpringBoot2.0java生成word文档(项目源码freeMarker实现)

1.需求说明

word中含有40几个需要填充的数据项点,以及10几个表格的数据需要生成

部分截图如下

SpringBoot2.0java生成word文档(项目源码freeMarker实现)

SpringBoot2.0java生成word文档(项目源码freeMarker实现)

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文档如下

数据项

SpringBoot2.0java生成word文档(项目源码freeMarker实现)

表格

SpringBoot2.0java生成word文档(项目源码freeMarker实现)

模板制作完成之后,只需要使用freemaker填充数据即可

以下代码参考网上的一些博客,但是有一个坑要做说明

SpringBoot2.0java生成word文档(项目源码freeMarker实现)

网上代码都是用的上面一行来处理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();
        }
    }
}

5.测试结果

SpringBoot2.0java生成word文档(项目源码freeMarker实现)

SpringBoot2.0java生成word文档(项目源码freeMarker实现)