java根据模板生成docx文件的方法(原创)

java快速导出word文档 在 Java 应用程序中,有时候我们需要将数据导出为 Word 文档,以便进行文档的编辑、打印或共享。本文将介绍如何使用 Java 实现导出 Word 文档的方法,帮助你灵活处理文档导出需求。 阅读详情

第一次在优快云发博,编辑器使用的不熟练,如大家感觉排版上有影响视觉的问题,欢迎私信或留言指出。

使用freemarker可以很方便的生成xml格式的doc文件,操作很简单,在word中写好需生成的文件,其中需替换的变量使用${}包裹,然后保存为xml格式,最后在java中开发代码读取数据并使用freemarker就可以生成需要的doc文件。然而,这个doc文件是xml格式的,部分手机上无法打开正常阅读。本文所要说的是如何生成在手机上可以查看的docx文件以及在其中增加图片。

继续刚才的操作,在word中保存为xml格式的同时保存为docx格式备用。使用文本编辑器打开xml文件,再使用网上的“xml格式化”工具将xml文件格式化。格式化后,查找其中的$符,可以看到,大多数的$都没有和{相邻,需要手工进行修改。修改为类似于这样的(避免无法被freemarker识别):

<w:r>
  <w:rPr>
    <w:rFonts w:asciiTheme="minorHAnsi" w:eastAsiaTheme="minorHAnsi" w:hAnsiTheme="minorHAnsi" w:hint="eastAsia"/>
  </w:rPr>
  <w:t>${dataabc}</w:t>
</w:r>

此时,xml文件依然可以使用word打开,但如果进行了下面的修改操作,word将无法打开此类文件,所以,如果今后需求发生变化需要修改模板时,以上步骤需要从头再来一次。

如果文件中有需要循环显示的内容(比如一个表格的行需要动态增加),需要手工修改xml文件的内容,在需要循环的内容前加入

<#list datas as onedata>

在结束位置加入

</#list>

最终效果类似于(这个例子是需要循环显示数据的表格。为减少篇幅,删除了一些不重要的内容,如需完整版请参考下载的附件):

<#list datas as onedata>
      <w:tr w:rsidR="00B925F3" w:rsidRPr="003E2683" w14:paraId="3CF1874A" w14:textId="77777777" w:rsidTr="00B925F3">
        <w:tc>
          <w:p w14:paraId="136DB865" w14:textId="478A8BA5" w:rsidR="00B925F3" w:rsidRPr="003E2683" w:rsidRDefault="00B925F3" w:rsidP="00DA2894">
            <w:r w:rsidRPr="003E2683">
              <w:rPr>
                <w:rFonts w:asciiTheme="minorHAnsi" w:eastAsiaTheme="minorHAnsi" w:hAnsiTheme="minorHAnsi" w:hint="eastAsia"/>
                <w:sz w:val="18"/>
                <w:szCs w:val="18"/>
              </w:rPr>
              <w:t>${onedata.no}</w:t>  <!-- no是代表list中的序号 -->
            </w:r>
          </w:p>
        </w:tc>
        <w:tc>
          <w:p w14:paraId="7347AE3D" w14:textId="77B282D9" w:rsidR="00B925F3" w:rsidRPr="003E2683" w:rsidRDefault="00B925F3" w:rsidP="00DA2894">
            <w:r w:rsidRPr="003E2683">
              <w:rPr>
                <w:rFonts w:asciiTheme="minorHAnsi" w:eastAsiaTheme="minorHAnsi" w:hAnsiTheme="minorHAnsi" w:hint="eastAsia"/>
                <w:sz w:val="18"/>
                <w:szCs w:val="18"/>
              </w:rPr>
              <w:t>${onedata.cell1}</w:t>
            </w:r>
          </w:p>
        </w:tc>
<!-- 省略若干<w:tc> </w:tc> -->
      </w:tr>
</#list>
<!-- list结束标记 -->

如果文件中有需要根据某变量是否为空而进行显示的内容,使用if进行判断(if标签内的内容即是变量parameter1不为空的时候所需要显示的内容):

<#if (parameter1)??> 
</#if>

在我的项目里,这个判断是用于是否显示图片,如果传入的parameter1不为空需要显示图片,否则,无需显示。If判断在xml中的具体位置请参考附件中的xml文件。

准备好了xml文件之后,使用freemarker先测试一下,看是否可以正确生成你所需要的word文件。这个方式生成的word文件,用文本编辑器是可以打开的,打开后的内容就是xml格式的。同时此文档也是word可以打开的(这是必须的)。

java使用freemarker生成word文件的核心代码如下所示(完整测试类请参考附件):

public String printTest(String Id) {  
//实际的代码可能会传入一个数据id,方法内进行查询,或者直接传入一个对象,方法内使用对象的属性
    Map<String,Object> dataMap = new HashMap<String, Object>(16);
    Map<String,Object> forMap = new HashMap<String, Object>(16);   //循环所需要的值
    List<Map<String,Object>> lstforMap = new ArrayList<Map<String,Object>>();
    Writer outWriter = null;
    File dataFile = null;
    String now = String.valueOf(System.currentTimeMillis());
    try {
        dataMap.put("param1", "data0");
        dataMap.put("param2", "data1");
        dataMap.put("param3", "data2");
        dataMap.put("param4", "yyyy年MM月dd日");
        //生成二维码图片,以一些数据的组合作为字符串进行二维码图片生成
        String[] qrcodeArray = {"test", "this is a test qrcode", "qrcode no chinese", "20202020"};
        String strQrcode = StringUtils.join(qrcodeArray, ",");
        String strQrcodebase64 = getCodeImageBase64(strQrcode, 200, 200);
        dataMap.put("qrcodestr", strQrcodebase64);
        GenerateImage(strQrcodebase64, "/app/" + File.separator + "abcd-" + now + ".png");          
        String[] lstArray = {"循环数据1", "循环数据2", "循环数据3", "循环数据4", "循环数据5"};
        //这个循环可以自行扩展,list里是对象,循环内取出放到不同的key值中
        for (String one : lstArray) {
            forMap = new HashMap<String, Object>(16);
            forMap.put("leadername", one);
            lstforMap.add(forMap);
        }
        dataMap.put("leader", lstforMap);
        //Configuration 用于读取ftl文件
        Configuration configuration = new Configuration(new Version("2.3.0"));
        configuration.setDefaultEncoding("utf-8"); 
        //根据某个类的相对路径指定
        configuration.setClassForTemplateLoading(this.getClass(), "");
        //输出文档路径及名称
        dataFile = new File("/app/" + File.separator + "abcd-" + now + ".doc"); 
        //以utf-8的编码读取xml文件
        Template template = configuration.getTemplate("test.xml", "utf-8");
        outWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dataFile), "utf-8"), 10240);
        template.process(dataMap, outWriter);            
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    } finally {
        System.gc();    //回收资源
        if (outWriter != null) {
            try {
                outWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }                
        }
    }
    return "/app/" + File.separator + "abcd-" + now + ".doc";
}

模板文件的内容详见附件中的范例,文件路径:src\com\test\docx\create\test.xml

生成的结果类似于下图
测试生成的word文档

然而,手机上打开此文档(在手机微信中打开),会显示成这个样子
糟糕的移动端体验

以上的测试是为了确认xml文件可以正确的生成doc文件。
接下来把第一步生成的docx文件使用winrar软件打开,右键,打开方式,选择其他应用程序,找到winrar即可。
打开之后,进入word目录,将其中的document.xml拖出来,用文本编辑器打开,使用xml格式化网站将此文件的内容格式化。

然后再用文本编辑器打开,重复刚才的手工修改xml文件内容的操作(包括循环、根据变量值显示内容等),将此文件内容修改为你所需要的模板格式。也可以直接进行这个替换步骤,毕竟最终的word文件什么样子与这一步是直接关联的。

此处注意的是不能使用刚才的xml文件替换此文件的内容。虽然都是xml文件格式,但内容存在很大差异。

修改好之后,将新的xml文件仍然放到和实现类文件同一目录,将docx文件放到resource目录,然后就是编写代码。

简单说一下原理(百度到的,但很抱歉没及时记录出处),重点是图片文件的替换(原创)。

docx文件其实是一个zip压缩文件,为了达到生成docx文件的目标,将此文件中的xml文件及图片文件使用zip文件的操作方法做替换

所以,将一个正常编写的docx文件中的xml文件取出,修改为freemarker所需要的xml模板文件,程序使用此模板文件生成doc文件(xml格式)后,再把此文件放到docx(zip文件)的相应目录,再改名为docx扩展名的文件。

对于图片文件,也是使用程序生成png文件,然后放到docx文件中的相应目录(word\media目录下)。

在上面的代码下增加如下的代码进行测试(filename变量即是上面的代码的返回值)

String filename = "/app/" + File.separator + "abcd-" + now + ".doc";        
String strReturn = "";
try {
    // abcd.doc.xml 其实是一个改了扩展名的docx文件,就是最初保存的docx文件
    InputStream inputStream = this.getClass().getResourceAsStream("/" + "abcd.doc.xml") ;
    // 用这种方式可以找到jar包中的模板文件
    ZipInputStream zipInputStream = ZipUtil.wrapZipInputStream(inputStream);
    // 打开已有的zip文件(其实是一个word文件,docx格式)
    ZipOutputStream zipOutputStream = ZipUtil.wrapZipOutputStream(new FileOutputStream(new File(filename + "tmp")));
    // 新生成的zip文件(扩展名是doctmp)
    String itemname = "word/document.xml";
    // 需要替换的文件在zip文件中的位置及文件名
    ZipUtil.replaceItem(zipInputStream, zipOutputStream, itemname, new FileInputStream(new File(filename)));
    // 把新生成的文件放回去   
    File pngfile = new File(filename.replaceAll("doc", "png"));
    if (pngfile.exists()) {
        // 如果需要放入的二维码图片文件存在
        zipInputStream = ZipUtil.wrapZipInputStream(new FileInputStream(new File(filename + "tmp")));
        // 打开上面生成的zip文件(扩展名是doctmp)
        zipOutputStream = ZipUtil.wrapZipOutputStream(new FileOutputStream(new File(filename + "x")));
        // 最终输出的文件,扩展名是docx
        itemname = "word/media/image1.png";
        // 需要替换的文件在zip文件中的位置及文件名
        ZipUtil.replaceItem(zipInputStream, zipOutputStream, itemname, new FileInputStream(new File(filename.replaceAll("doc", "png"))));
        // 把图片文件放到zip包 ,目前这个是同一文件中相同的图片文件
    } else {
        // 如果没有二维码图片
        File tmpfile = new File(filename + "tmp");
        File docxfile = new File(filename + "x");
        tmpfile.renameTo(docxfile);
        // 把上面的临时文件改名为docx扩展名的文件,此文件即是最终返回的文件名
    }
    File tmpFile = new File(filename + "tmp");
    if (tmpFile.exists()) {
        tmpFile.delete();
    }
    if (pngfile.exists()) {
        pngfile.delete();
    }
    File tmpFile2 = new File(filename);
    if (tmpFile2.exists()) {
        tmpFile2.delete();
    }
    //删除临时文件    
    strReturn = filename + "x";
} catch (Exception e) {
    strReturn = "Exception: " + e.toString();
}
return strReturn;

执行之后,生成的word文件和第一次的并没有外观上的直接差异,但在手机上可以直接打开了。

本文遗留问题:
一个word文档中如果有多个不同的图片,未研究需要如何处理,有此需求的朋友们可以自行研究解决方案。

参考资料
FreeMarker 中文官方参考手册 (http://freemarker.foofun.cn/toc.html)
FreeMarker中if标签内的判断条件 (https://www.iteye.com/blog/lj6684-1594769)
Xml文档在线格式化(https://tool.oschina.net/codeformat/xml)

演示项目下载地址:
csdn下载

JAVA通过xml模板生成DOCX文档 准备工作: 1、xml模版,获取方式为docx文件winrar方式打开,取出document.xml 2、将内容格式化后修改需要替换的内容为freemarker标签,如下: 3、文件准备好后存放到某个目录下,docx文件为将要生成好的docx文件样式模版,必须要有的。 4、准备程序代码 import freemarker.template.Configuration; import free... 阅读详情

相关推荐

Springboot -- 按照模板生成docx、pdf文件,docx转pdf格式

xdocreport 也支持根据模板导出 word ,但是 poi-tl 的功能更齐全,操作更简单,文档清晰。poi-tl 、xdocreport 内部均依赖了 poi ,要注意两者中 poi 和 自身项目引用的 poi 的版本是否存在冲突。

zhangdm的博客 4748

OFFICE技术讲座:设置调整字间距(kern/kerning)后,标点就不压缩

  一个文档,一行标点压缩,一行标点不压缩。吾觉得奇怪,于是就把文档解压后看了一下document.xml,内容如下: <w:p w14:paraId="47202302" w14:textId="77777777" w:rsidR="00D93D69" w:rsidRPr="00D93D69" w:rsidRDefault="00D93D69" w:rsidP="00D93D69"> <w:r w:rsidRPr="00D93D69"> <w:rPr>

柳鲲鹏 1489

java使用freemark实现word(.doc/.docx)/pdf生成和导出(附源码和模板文件)

freemark生成word/pdf一、 背景二、实现的技术选型以及遇到的坑三、最终的效果2.1 `.doc` word效果展示2.1 `.docx` word效果展示2.2 docx word转pdf效果展示三、准备工作及代码实现3.1后缀为.doc的word生成3.2 后缀为.docx的word生成,word转pdf四、相关源码资源下载中间参考链接 一、 背景 工作需要,需要实现word和p...

小雪的博客 6246

Springboot实现Freemarker生成word文档

导入Freemark工具包 <!-- freemaker引擎 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> 配置Freemarker工具类 import java.io.BufferedWrite

pbl13071138970的博客 1205

Java通过docx模板生成docx

Java通过docx模板生成docx 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客: ...

weixin_39783104的博客 1765

java如何根据模板填充数据生成word文档

java如何根据模板填充数据生成word文档

qq_45890619的博客 5558

java poi根据模板生成docx

一、实现功能 1、支持表格、图像、插入页脚,其中图像包含base64 位和 只传入图像路径 2、要求使用对应的模板 故只能作为参考 3、里面的以下判断时根据 模板文件里面的key进行的特殊处理 二、代码(版本 3.15) package com.hyzs.szcg.doc.utils; import java.awt.image.BufferedImage; import java....

z446981439的博客 2107

Java动态生成模板

一、制作模板https://blog.youkuaiyun.com/super_DuoLa/article/details/109338707 二、代码核心 1、将对象转成map格式,方便赋值 package test.util; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.A

心若向阳 无谓悲伤 2749

Java通过Freemarker生成docx文件

一:doc和docx文件 首先我们要了解doc和docx两种word文件的相同点和不同点,为什么ftl可以直接生成doc而生成docx就会报错。 1.doc和docx文件构成 我们可以把doc格式的word文件当成是一个单独的xml文件,而docx当成一个zip压缩包。将一个docx文件的后缀名改成.zip,然后用压缩工具打开,显示的即为docx的目录结构。 2.为什么ftl能直接转成doc...

yuchenff的博客 4277

使用Java导出Word文档

Apache POI是一个开源的Java库,可以处理各种Microsoft Office文件格式,包括Word文档(.doc和.docx)。请注意,上述示例代码只是演示了导出Word文档的基本步骤。根据实际需求,您可以使用Apache POI库提供的更多功能来自定义和格式化文档的内容,例如添加表格、插入图像、设置字体样式等。在Java代码中,我们需要创建一个Word文档对象来表示要导出的文档。以上代码示例创建了一个标题为"这是一个标题"的Word文档,并添加了一个段落"这是一个段落。步骤5:完整示例代码。

CodeShiftZ的博客 1037

java导出docx导出pdf

亲自可用,包含jar包导入项目即可用,可以生成docx也可以生成pdf

JAVA通过模板生成DOCX文档(2)

第一篇链接 看第一篇文章会对原理更理解一些,但是比较繁琐,所以写了这一篇,怎么获取和修改xml在上一篇都有,不赘述了 import freemarker.template.Configuration; import freemarker.template.Template; import java.io.*; import java.util.Enumeration; import java

无人问津的博客 4599

Win10右键新建菜单丢了记事本?别慌!5分钟教你用注册表找回来(附权限设置避坑指南)

本文详细指导如何通过注册表编辑器恢复Win10右键新建菜单中丢失的记事本选项,包括重建键值结构、配置ShellNew项及设置权限等关键步骤。特别提供权限避坑指南,确保修改持久有效,帮助用户快速解决这一常见但困扰的问题。

weixin_31714129的博客 856

Springboot —— 根据docx填充生成word文件,并导出pdf

docx模板填充数据,以及导出pdf类型文件

专注写bug 6430

告别ArcGIS小红叉:从‘无法验证登录信息’到成功连接在线资源的完整排错记录

本文详细记录了从Windows便签的网络错误到ArcGIS连接问题的完整排错过程。通过分析代理服务器、DNS配置和系统网络设置,解决了ArcGIS无法验证登录信息的问题,提供了实用的网络优化方案和技术排错思路。

weixin_26800111的博客 389

Vben Admin权限管理全解析:从动态菜单到按钮级控制(附代码示例)

本文全面解析Vben Admin框架的权限管理系统,从动态菜单生成到按钮级细粒度控制,提供完整的实现方案和代码示例。深入探讨基于角色-权限模型的架构设计,分享前后端协作的最佳实践,帮助开发者快速构建安全高效的企业级后台系统。

weixin_30919429的博客 485

java生成docx_JAVA通过模板生成DOCX文档

import java.io.File;import java.io.IOException;import java.io.Writer;import java.util.Map;import freemarker.template.Configuration;import freemarker.template.Template;import freemarker.template.Templa...

weixin_33895396的博客 901

对西安交大轴承数据集XJTU-SY_Bearing_Datasets进行读取和处理:

故障诊断,对西安交大轴承数据集XJTU-SY_Bearing_Datasets进行读取和处理

qq_44757503的博客 6060

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

之前写过一篇利用Freemarker模板生成doc的博客,不过那个博客有点缺陷,生成的word占用的空间很大,几百页的word有将近100M了。所以,后面需求必须是生成的docx文档,结果导出后正常才几M,昨天花了一天的时间实现。具体思路1.把docx文档修改为ZIP格式(修改.docx后缀名为.zip)2.获取zip里的document.xml文档以及_rels文件夹下的document.xml...

weixin_39827589的博客 807

别再为环保数采仪通讯发愁!手把手教你用MCGS触摸屏+4G模块发送HJ212报文

本文详细介绍了如何使用MCGS触摸屏配合4G模块高效发送HJ212标准报文,解决工业环保监测中的数据采集与传输难题。从硬件连接到软件配置,再到报文组装与发送策略,提供全流程实用指南,特别针对4G网络环境下的特殊设置给出优化建议,帮助工程师提升数据传输稳定性与效率。

weixin_30847865的博客 389

从摄像头选型到PCB布线:搞懂MIPI CSI带宽计算,避免你的嵌入式项目翻车

本文详细解析了MIPI CSI带宽计算在嵌入式项目中的关键作用,从摄像头选型到PCB布线提供全流程指导。通过实际案例和计算公式,帮助工程师准确计算带宽需求,避免图像丢帧和系统崩溃问题,同时优化PCB设计确保信号完整性。

weixin_30533109的博客 375

JAVA根据word模板生成合同,并能实现网页在线浏览/打印/下载

最近, 项目有这样一个需求: 根据我选择的模板(docx文件),和我表单填的数据,生成相应的合同文件(docx),该合同要能网页在线浏览/打印/下载在合同中还要放置签字图片和身份证图片 我的实现: 1上传一份合同模板,在用户提交表单数据的时候,把数据写进合同模板,生成完整合同单独保存。这里需要用到docx模板插件能插入图片的那种(身份证读卡器和签字生成的图片要放进合同里)...

质疑那些理所当然,才是理所当然! 1万+

【深度学习基础】一篇弄懂训练集、验证集和测试集(呕心沥血版)

在深度学习中,训练集、验证集和测试集是构建和评估模型的核心组成部分。训练集用于模型的学习和参数优化,验证集帮助调整超参数和防止过拟合,而测试集则用于最终评估模型的泛化性能。正确划分和使用这三类数据集,对于开发高效、可靠的深度学习模型至关重要。本文将深入解析它们的作用、划分方法以及在实际应用中的注意事项,帮助你全面理解并掌握数据集的科学使用方法,从而提升模型的实际表现。

十二月的猫 3792

使用java Apache poi 根据word模板生成word报表

使用java Apache poi 根据word模板生成word报表 使用poi读取word模板,替换word中的{text}标签,并根据自定义标签循环生成表格或表格中的行。 代码示例下载:https://download.youkuaiyun.com/download/u012775558/10306308 注意,仅支持docx格式的word文件,大概是word2010及以后版本,doc格式不支持。 ...

u012775558的博客 3万+

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

java通过模板导出docx文档

@ java通过模板导出docx文档 二、使用步骤 代码如下(示例): import freemarker.template.Configuration; import freemarker.template.Template; import java.io.*; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.zip.ZipEntry; import ja

qq_39493446的博客 828

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

lirc
博客等级 码龄25年 0粉丝 1原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值