java利用Freemarker模板生成docx格式的word文档

利用Freemarker模板生成docx

思路

1.把docx文档修改为ZIP格式(修改.docx后缀名为.zip) 2.获取zip里的document.xml文档以及_rels文件夹下的document.xml.rels文档 3.把内容填充到document.xml里 4.在输入docx文档的时候把填充过内容的的 document.xml用流的方式写入zip(详见下面代码)。 5.输出docx文档 docx模板修改成zip格式后的信息如下(因为word文档本身就是ZIP格式实现的)

  • document.xml里存放主要数据

处理流程

1.准备好docx模板

2.把docx文档修改为ZIP格式(修改.docx后缀名为.zip)

3.获取zip文件里的word文件夹下的document.xml文件作为模板。

4.填充模板信息。

5.输出word测试

代码

       <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.WordUtils;

import java.io.*;
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.ZipFile;
import java.util.zip.ZipOutputStream;

@Slf4j
public class DocUtils {
    public static void main(String[] args) {
        OutputStream outputStream = null;
        try {
            outputStream= new FileOutputStream("e:/test.docx");
            Map dataMap = new HashMap();
            dataMap.put("ah","1231231");
            dataMap.put("dsrmc","456");
            createDocx(dataMap,outputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
        }


    }
    //outputStream 输出流可以自己定义 浏览器或者文件输出流
    public static void createDocx(Map dataMap, OutputStream outputStream) {
        ZipOutputStream zipout = null;
        try {
            //内容模板
            ByteArrayInputStream documentInput = getFreemarkerContentInputStream(dataMap);
            //最初设计的模板
            File docxFile = new File("E:\\template.docx");
            if (!docxFile.exists()) {
                docxFile.createNewFile();
            }
            ZipFile zipFile = new ZipFile(docxFile);
            Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();
            zipout = new ZipOutputStream(outputStream);
            //开始覆盖文档------------------
            int len = -1;
            byte[] buffer = new byte[1024];
            while (zipEntrys.hasMoreElements()) {
                ZipEntry next = zipEntrys.nextElement();
                InputStream is = zipFile.getInputStream(next);
                if (next.toString().indexOf("media") < 0) {
                    zipout.putNextEntry(new ZipEntry(next.getName()));
                    //如果是word/document.xml由我们输入
                    if ("word/document.xml".equals(next.getName())) {
                        if (documentInput != null) {
                            while ((len = documentInput.read(buffer)) != -1) {
                                zipout.write(buffer, 0, len);
                            }
                            documentInput.close();
                        }
                    } else {
                        while ((len = is.read(buffer)) != -1) {
                            zipout.write(buffer, 0, len);
                        }
                        is.close();
                    }
                }
            }

        } catch (Exception e) {
            log.error("word导出失败:",e);
        }finally {
            if(zipout!=null){
                try {
                    zipout.close();
                } catch (IOException e) {
                    log.error("io异常");
                }
            }
            if(outputStream!=null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error("io异常");
                }
            }
        }
    }
    /**
     * 获取模板字符串输入流
     * @param dataMap   参数
     * @return
     */
    public static ByteArrayInputStream getFreemarkerContentInputStream(Map dataMap) {
        ByteArrayInputStream in = null;
        try {
            TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig());
            String templateStr= FileUtil.readString(new File("E:\\document.xml"),"utf-8");
            Template template = engine.getTemplate(templateStr);
            String result = template.render(dataMap);
            //这里一定要设置utf-8编码 否则导出的word中中文会是乱码
            in = new ByteArrayInputStream(result.getBytes("utf-8"));
        } catch (Exception e) {
            log.error("模板生成错误!");
        }
        return in;
    }
}