Java通过Freemarker生成docx文件

本文详细介绍了doc和docx文件的区别,以及为何FTL能生成doc但不能直接生成docx。通过解析docx文件的zip结构,展示了如何使用Freemarker结合Java将XML模板转换为docx文件,包括关键代码示例。最后,提到了后续将分享如何在docx文件中插入docx格式的word文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:doc和docx文件

首先我们要了解doc和docx两种word文件的相同点和不同点,为什么ftl可以直接生成doc而生成docx就会报错。

1.doc和docx文件构成

  • 我们可以把doc格式的word文件当成是一个单独的xml文件,而docx当成一个zip压缩包。将一个docx文件的后缀名改成.zip,然后用压缩工具打开,显示的即为docx的目录结构。
    将一个docx文件的后缀名改成.zip,然后用压缩工具打开,显示的即为docx的目录结构。

2.为什么ftl能直接转成doc而不能直接转成docx

  • ftl生成word文件的过程是通过java选定一个*.ftl模板文件(该ftl模板是通过doc文件另存为xml格式,然后把后缀改为ftl得到),加入map参数集合,调用freemaker组件,生成一个doc格式的word文件。
  • 综合前边概念,我们可以得出结论,freemaker只能通过模板生成一个doc格式的独立文件,而无法得到docx所需要的zip格式,所以ftl无法直接生成docx文件。

3.docx文件格式解析

  • 选一个word文件作为输出模板,然后java代码输出需要提取word压缩包里两个文件:
    1.word路径下的document.xml
    2.word路径下_rels下的document.xml.rels
    在这里插入图片描述
    在这里插入图片描述

  • 将docx文件的后缀改为.zip之后,把word路径下的document.xml作为模板,可通过格式化工具把xml文件里的数据格式化,点开后编写word的xml内容,下图以一个段落为例,其他类型请自行参考openxml语法或自己在word中对应
    在这里插入图片描述

  • 编写完xml模板之后,进入代码开发阶段,
    1.同ftl生成doc一样,第一步要先封装数据到一个map中
    2.指定输出docx的文件路径
    3.指定两个从word压缩包中提取的文件路径
    4.执行输出
    代码示例如下:

	//封装数据
	Map<String,Object> dataMap=new HashMap<>();
	dataMap.put("title","示例");//key名对应xml模板里的freemaker标签
	//输出docx的模板
	String originalTemplate = tempPath+ File.separator+"needsOriginalTemplate.docx";
	//word中提取的两个文件,代码中可以根据实际情况更改名字
	String documentXmlRels = "document.xml.rels";
	String document = "document.xml";
	//执行输出
	Xml2DocxUtil.Xml2Docx(outFile,dataMap,document,documentXmlRels,originalTemplate);

下方附上 Xml2DocxUtil.java 以及FreeMarkUtils.java

Xml2DocxUtil.java


import cn.hutool.core.io.FileUtil;

import java.io.*;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class Xml2DocxUtil {
   /**
    *
    * @param outFile 输出文件
    * @param dataMap 数据
    * @param document 模板文件
    * @param documentXmlRels 模板关联文件
    * @param originalTemplate 模板原始docx文件
    * @return retMap succ 是否成功 true & false ; msg返回异常信息
    */
   public static Map<String,Object> Xml2Docx(File outFile, Map<String,Object> dataMap, String document, String documentXmlRels, String originalTemplate){
       Map<String,Object> retMap = new HashMap<>();
       retMap.put("succ",true);
       ZipOutputStream zipout = null;
       OutputStream outputStream = FileUtil.getOutputStream(outFile);
       try {
           //图片配置文件模板
           ByteArrayInputStream documentXmlRelsInput = FreeMarkUtils.getFreemarkerContentInputStream(dataMap, documentXmlRels);
           //内容模板
           ByteArrayInputStream documentInput = FreeMarkUtils.getFreemarkerContentInputStream(dataMap, document);
           ZipFile zipFile = new ZipFile(originalTemplate);
           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()));
                   if (next.getName().indexOf("document.xml.rels") > 0) { //如果是document.xml.rels由我们输入
                       if (documentXmlRelsInput != null) {
                           while ((len = documentXmlRelsInput.read(buffer)) != -1) {
                               zipout.write(buffer, 0, len);
                           }
                           documentXmlRelsInput.close();
                       }
                   } else if ("word/document.xml".equals(next.getName())) {//如果是word/document.xml由我们输入
                       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) {
           e.getStackTrace();
           retMap.put("succ",false);
           retMap.put("msg",e.getMessage());
       }
       finally {
           if(zipout!=null){
               try {
                   zipout.close();
               } catch (IOException e) {
                   e.getStackTrace();
                   retMap.put("succ",false);
                   retMap.put("msg",e.getMessage());
               }
           }
           if(outputStream!=null){
               try {
                   outputStream.close();
               } catch (IOException e) {
                   e.getStackTrace();
                   retMap.put("succ",false);
                   retMap.put("msg",e.getMessage());
               }
           }
       }
       return retMap;
   }
}

FreeMarkUtils.java


import freemarker.template.Configuration;
import freemarker.template.Template;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;

import java.util.Map;

public class FreeMarkUtils {
   private static Logger logger = LoggerFactory.getLogger(FreeMarkUtils.class);

   public static Configuration getConfiguration(){
       //创建配置实例
       Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
       //设置编码
       configuration.setDefaultEncoding("utf-8");
       configuration.setClassForTemplateLoading(FreeMarkUtils.class, "/templates/needsFileTemplates/");
       return configuration;
   }

   /**
    * 获取模板字符串输入流
    * @param dataMap   参数
    * @param templateName  模板名称
    * @return
    */
   public static ByteArrayInputStream getFreemarkerContentInputStream(Map dataMap, String templateName) {
       ByteArrayInputStream in = null;
       try {
           //获取模板
           Template template = getConfiguration().getTemplate(templateName);
           StringWriter swriter = new StringWriter();
           //生成文件
           template.process(dataMap, swriter);
           in = new ByteArrayInputStream(swriter.toString().getBytes(StandardCharsets.UTF_8));//这里一定要设置utf-8编码 否则导出的word中中文会是乱码
       } catch (Exception e) {
           e.printStackTrace();
           logger.error("模板生成错误!");
       }
       return in;
   }
}

欢迎大家评论区进行交流,之后会发表一篇如何通过java在docx的word文件中插入一个docx格式的word文件的文章。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值