乐于分享
好东西不私藏

标文,法律性的内容导出word方案实现

标文,法律性的内容导出word方案实现

有阵子没有更新公众号了,这篇文章是新年的第一篇文章。这篇文章主要是关于将页面上的内容动态的数据部分填充到模板里,目前市场上有两种方向,一种是导出pdf的,另一种就是导出word的。关于导出pdf的,之前文章有介绍,这里就不再赘述,可以通过表格导入读取图片&pdf表单生成这个快速查阅。接下来主要是介绍word方向的探讨。
首先再介绍word之前,我们得先了解,pdf和word的区别以及它们应用场景原理。像一般的内容比较固定的,可以采用pdf,但是内容不固定的采用的是word,这个和pdf/word原理有关系,pdf它采用的是绝对定位,word采用的是相对定位,这也就决定了pdf导出来的时候版式会好看点,但是相对的带来的弊端就是没有办法自适应,比如要填充的内容很多,pdf方式没有办法做到自动跨行,但是word可以。所以具体使用哪种方案,和应用场景有关系,选择最合适的。
关于word实现,目前比较多的分为三类。一类是poi-tl,第二类是vba进行二开,第三类就是freemarker+xml方式。关于这三类不同点,整理了相关资料进行汇总,如下:
然后推荐应用场景如下:
根据具体的需要选择,项目中使用freemarker+xml技术比较能够很好的满足要求,样式也相对来说ok,对样式要求比较严,功能多的可以考虑poi-tl
接下来围绕freemarker+xml技术进行讲解,讲解之前首先需要明确的就是如下要求:

FreeMarker 本⾝不直接⽀持 Word 模板解析,核⼼原理是:先制作标准 Word ⽂档(.docx),将其

另存为 XML 格式,在 XML 中嵌⼊ FreeMarker 占位符(${变量名})、指令(循环、判断等),再通

过 FreeMarker 引擎渲染 XML,最终将渲染后的 XML 重命名为 .docx,即为动态⽣成的 Word ⽂件。

关键前提:仅⽀持 .docx 格式(2007及以上版本),不⽀持 .doc 格式(⼆进制格式,⽆法嵌⼊占位符);模板制作的核⼼是「保证 XML 结构完整,占位符嵌⼊位置正确」。

需关注导出的word在office与wps两种产品工具打开的对比效果。

步骤1:制作基础 Word ⽂档

1. ⽤ WPS/Word 新建⽂档,编辑固定内容(如标题、表格表头、落款等,即不需要动态替换的部分)。

2. 预留动态内容位置(如姓名、⽇期、表格数据、列表等),暂时⽤占位⽂本标记(如【姓名】【表格数据】),⽅便后续替换为 FreeMarker 语法。

3. 注意:避免使⽤特殊格式(如艺术字、复杂公式、嵌⼊图⽚),此类内容在 XML 中结构复杂,易导致渲染失败;如需图⽚,后续单独说明。

步骤2:将 Word 另存为 XML 格式

1. 打开制作好的 Word ⽂档,点击「⽂件」→「另存为」,选择保存类型为「Word XML ⽂档(*.xml)」,保存到本地(如 template.xml)。

2. 关键:保存时不要选择「启⽤宏的 Word XML ⽂档」,也不要压缩,选择纯 XML 格式。

步骤3:编辑 XML,嵌⼊ FreeMarker 语法

⽤记事本、VS Code 等⽂本编辑器打开保存的 template.xml,找到步骤1中标记的占位⽂本,替换为FreeMarker 占位符和指令,核⼼语法如下(重点):

3.1 基础占位符(静态⽂本替换)

⽤于替换单个静态内容(如姓名、⽇期、编号等),直接嵌⼊ ${变量名},注意:不要破坏 XML 原有的标签结构。⽰例:原 XML 中对应「姓名」的内容:<w:t>【姓名】</w:t>替换后:<w:t>${userName}</w:t>

其他⽰例:${orderNo}(订单号)、${createTime}(创建时间)、${companyName}(公司名称)

3.2 循环指令(动态表格/列表)

⽤于动态⽣成表格⾏、列表项(如订单明细、⽤⼾列表),核⼼使⽤ <#list 集合 as 元素>…</#list>

接下来进入到代码实现环节,首先第一步就是pom文件引入:

<dependency>      <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-starter-freemarker</artifactId>   <version>2.7.18</version></dependency> <dependency>       <groupId>commons-io</groupId>        <artifactId>commons-io</artifactId>      <version>2.11.0</version>  </dependency>

第二步就是整合工具类,FreemarkerUtils工具类

package cn.xx.xx.module.bill.utils;import freemarker.template.Configuration;import freemarker.template.Template;import freemarker.template.TemplateException;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Component;import javax.annotation.Resource;import java.io.*;import java.net.URLEncoder;import java.nio.charset.StandardCharsets;import java.time.LocalDateTime;import java.util.HashMap;import java.util.Map;@Componentpublic class FreeMarkerWordUtil {    @Autowired    private Configuration configuration;    /**     * 渲染模板⽣成 Word ⽂件     * @param templateName 模板名称(如 orderTemplate.ftl,放在    resources/templates/word/ ⽬录下)     * @param data 模板所需数据(Map 格式,key对应模板中的${变量名})     * @param outputPath ⽣成 Word 的保存路径(如 D:/output/order123.docx)     * @throws IOException 模板读取/⽂件写⼊异常     * @throws TemplateException 模板渲染异常     */    public void generateWord(String templateName, Map<String, Object> data,                             String outputPath) throws IOException, TemplateException {         // 1. 获取模板(指定模板路径,这⾥假设模板放在 templates/word ⽬录下)        Template template = configuration.getTemplate("word/" + templateName, StandardCharsets.UTF_8.name());        // 2. 渲染模板,⽣成 XML 字符串(使⽤ StringWriter 接收)        StringWriter stringWriter = new StringWriter();        BufferedWriter writer = new BufferedWriter(stringWriter);        template.process(data, writer); // 渲染数据到模板,⽣成XML        writer.flush();        String xmlContent = stringWriter.toString();        // 3. 将 XML 字符串写⼊⽂件,重命名为 .docx        try (OutputStream outputStream = new FileOutputStream(outputPath);             OutputStreamWriter outputStreamWriter = new                     OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) {            outputStreamWriter.write(xmlContent);            outputStreamWriter.flush();        }        // 关闭流        writer.close();        stringWriter.close();    }    /**     * 下载方式     *     */    public ResponseEntity<byte[]> downWordFile(BasicInfoDO basicInfo) throws IOException, TemplateException {        // 2. 渲染模板,⽣成 XML 字节数组        Template template = configuration.getTemplate("word/contract.ftl""UTF-8");        StringWriter stringWriter = new StringWriter();        Map<String, Object> data=new HashMap<>();        //组装数据......             template.process(data, new BufferedWriter(stringWriter));        byte[] xmlBytes =                stringWriter.toString().getBytes(StandardCharsets.UTF_8);        // 3. 构建响应头,实现下载        HttpHeaders headers = new HttpHeaders();        // 设置下载⽂件名(URLEncoder.encode 处理中⽂⽂件名)        String fileName = URLEncoder.encode("订单详情.docx""UTF-8");        headers.setContentDispositionFormData("attachment", fileName);        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);        // 4. 返回响应        return new ResponseEntity<>(xmlBytes, headers, HttpStatus.OK);    }    /**     * 文本截断 - 防止文本过长导致显示不完整     */    private String truncateText(String text, int maxLength) {        if (text == null) {            return "-";        }        if (text.length() > maxLength) {            return text.substring(0, maxLength) + "...";        }        return text;    }    /**     *     */    private String timeStr(LocalDateTime time){        return time.getYear()+"年"+time.getMonth().getValue()+"月"+time.getDayOfMonth()+"日";    }}

然后使用的时候,controller如下:

    @GetMapping("/downloadWord")    public ResponseEntity<byte[]> downloadWord(@RequestParam("id") Long id) throws IOException, TemplateException {       //。。。。。。       return  freeMarkerWordUtil.downWordFile( basicInfo);    }

最后效果如下:

效果还可以,能够自适应。好了,本文介绍到这里就结束了,最后鸣谢@wayn,@Ic,@天空海洋等各位的指点,以上内容有借鉴并参考以上好友的观点和内容,其他读者如果有更好的也欢迎留言区进行讨论。

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 标文,法律性的内容导出word方案实现

评论 抢沙发

8 + 5 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮