Java通过Freemarker生成docx文件

 2023-09-05 阅读 105 评论 0

摘要:一:doc和docx文件 首先我们要了解doc和docx两种word文件的相同点和不同点,为什么ftl可以直接生成doc而生成docx就会报错。 1.doc和docx文件构成 我们可以把doc格式的word文件当成是一个单独的xml文件,而docx当成一个zip压缩包。将一个docx文件的后缀名改

一: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文件的文章。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/210.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息