SpringBoot实现word通配符动态填充字段(.docx与.doc)

场景

在开发工作中可能会遇到一种需求就是需要把数据库或者动态的把字段值去把某个word文档中定义好的通配符给替换掉,比如签署一个文件模板,有些产品名称或者姓名都可以让系统给动态赋值上去等

那么开始直接上代码:

1.导入相关依赖 Apache POI

    <!-- Apache POI -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>4.1.2</version> 
    </dependency>

使用 XWPFDocument 去处理.docx文件,使用 HWPFDocument 去处理.doc文件

(word的老版本新版本处理方式不同)

2.具体实现:

import cn.sckr.overseas.funds.common.base.constant.HttpStatus;
import cn.sckr.overseas.funds.common.base.utils.exception.BusinessException;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.CharacterRun;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @author yfq
 */
public class TemplateReplaceUtil {
    /**
     * XWPFParagraph:处理文档段落相关的
     * XWPFTable:处理表格相关的
     * XWPFPicture :处理图片相关的
     * XWPFSDT:代表Structured Document Tag(SDT),这是一种结构化文档标记,可以包含模板控件、元数据等。
     * XWPFChart:代表图表,即插入在 Word 文档中的图表对象。
     * XWPFFootnote:代表脚注,表示文档中的脚注内容。
     * XWPFHyperlinkRun:代表超链接文本,表示文档中的超链接。
     * XWPFFooter 和 XWPFHeader:分别代表文档的页脚和页眉。
     * XWPFFootingPart 和 XWPFHeaderPart:这些类是页脚和页眉的具体部分,它们包含了实际的内容。
     * XWPFAbstractNum 和 XWPFFootnote:用于处理编号和脚注的抽象类。
     * @param bytes 文件的字节数组
     * @param placeholderMap 需要替换的键值对 key为通配符,value为要把通配符替换的值 如:key:'&*',value:'名字',就是把word中的 &* 替换成 名字
     * @return InputStream
     */
    public static InputStream selectCheckboxByPlaceholder(byte[] bytes, HashMap<Object, Object> placeholderMap) throws IOException {
        try {
            InputStream inputStream = new ByteArrayInputStream(bytes);

            // 使用 try-with-resources 确保资源关闭
            try (InputStream stream = inputStream) {
                // 尝试使用 XWPFDocument 处理
                try {
                    XWPFDocument document = new XWPFDocument(stream);
                    processDocument(document, placeholderMap);

                    // 将修改后的文档保存回Word文件
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    document.write(byteArrayOutputStream);
                    return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                } catch (Exception xwpfException) {
                    // 如果 XWPFDocument 处理失败,尝试使用 HWPFDocument 处理
                    try {
                        HWPFDocument document = new HWPFDocument(stream);
                        processDocument(document, placeholderMap);

                        // 将修改后的文档保存回Word文件
                        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                        document.write(byteArrayOutputStream);
                        return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                    } catch (Exception hwpfException) {
                        throw new BusinessException(HttpStatus.ERROR, "填充失败");
                    }
                }
            }
        } catch (IOException e) {
            throw new BusinessException(HttpStatus.ERROR, "填充失败");
        }
    }

    private static void processDocument(XWPFDocument document, HashMap<Object, Object> placeholderMap) {
        // 遍历段落和运行,查找和替换占位符
        for (XWPFParagraph paragraph : document.getParagraphs()) {
            for (XWPFRun run : paragraph.getRuns()) {
                String text = run.getText(0);
                for (Map.Entry<Object, Object> entry : placeholderMap.entrySet()) {
                    if (text != null && text.contains(String.valueOf(entry.getKey()))) {
                        // 用自定义字符表示选中状态替换占位符
                        text = text.replace(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
                        run.setText(text, 0);
                    }
                }
            }
        }

        // 遍历所有table
        for (XWPFTable table : document.getTables()) {
            for (XWPFTableRow row : table.getRows()) {
                for (XWPFTableCell cell : row.getTableCells()) {
                    for (XWPFParagraph paragraph : cell.getParagraphs()) {
                        for (XWPFRun run : paragraph.getRuns()) {
                            String text = run.getText(0);
                            for (Map.Entry<Object, Object> entry : placeholderMap.entrySet()) {
                                if (text != null && text.contains(String.valueOf(entry.getKey()))) {
                                    // 用自定义字符表示选中状态替换占位符
                                    text = text.replace(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
                                    run.setText(text, 0);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private static void processDocument(HWPFDocument document, HashMap<Object, Object> placeholderMap) {
        Range range = document.getRange();

        // 遍历段落和运行,查找和替换占位符
        for (int i = 0; i < range.numParagraphs(); i++) {
            org.apache.poi.hwpf.usermodel.Paragraph paragraph = range.getParagraph(i);
            for (int j = 0; j < paragraph.numCharacterRuns(); j++) {
                CharacterRun run = paragraph.getCharacterRun(j);
                String text = run.text();
                for (Map.Entry<Object, Object> entry : placeholderMap.entrySet()) {
                    if (text != null && text.contains(String.valueOf(entry.getKey()))) {
                        // 用自定义字符表示选中状态替换占位符
                        text = text.replace(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
                        run.replaceText(text,true);
                    }
                }
            }
        }
    }

3.代码解释

 

4.测试代码(因为这是业务需求,其实可以入参就是一个文件路径不必是byte[],处理完可以直接输出去)

    public static void main(String[] args) throws FileNotFoundException {
        String inputWordPath = "D:/test/test.docx";
        String outputWordPath = "D:/test/test1.docx";
        HashMap<Object, Object> placeholderMap = new HashMap<>();
        placeholderMap.put("&*",'☑');
        placeholderMap.put("&#",'☐');
        placeholderMap.put("#{name}","???????????????");
        placeholderMap.put("#{code}","11111111111111");
        File file = new File(inputWordPath);
        FileInputStream fileInputStream = new FileInputStream(file);
        byte[] bytes = new byte[(int) file.length()];
        try {
            InputStream inputStream = selectCheckboxByPlaceholder(bytes, placeholderMap);
            FileOutputStream fileOutputStream = new FileOutputStream(outputWordPath);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = inputStream.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, len);
            }
            inputStream.close();
            byteArrayOutputStream.close();
            byte[] bytess = byteArrayOutputStream.toByteArray();
            fileOutputStream.write(bytess);
//            saveModifiedDocument(inputWordPath, outputWordPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中,Controller的URL路径可以使用通配符来匹配不同的URL请求。下面介绍几种常见的URL通配符用法。 1. 单个路径变量:使用`{}`来表示路径变量,可以在URL中的任何位置进行使用,例如: ```java @GetMapping("/user/{id}") public User getUserById(@PathVariable("id") int id) { // 根据id获取用户信息 } ``` 上述代码中,`/user/{id}`表示一个路径变量,可以匹配类似`/user/1`或者`/user/2`的路径,并将路径中的id值作为参数传递给`getUserById`方法。 2. 多个路径变量:可以在URL中同时使用多个路径变量,例如: ```java @GetMapping("/user/{id}/info/{name}") public String getUserInfo(@PathVariable("id") int id, @PathVariable("name") String name) { // 根据id和name获取用户信息 } ``` 上述代码中,`/user/{id}/info/{name}`表示两个路径变量,可以匹配类似`/user/1/info/john`或者`/user/2/info/smith`的路径,并将路径中的id和name值作为参数传递给`getUserInfo`方法。 3. 通配符匹配:使用`*`进行通配符匹配,例如: ```java @GetMapping("/user/*/info") public List<User> getAllUserInfo() { // 获取所有用户信息 } ``` 上述代码中,`/user/*/info`表示匹配以`/user/`开头,以`/info`结尾,并且中间可以是任意字符的路径。 4. Ant风格通配符:使用`?`或`**`进行Ant风格的通配符匹配,例如: ```java @GetMapping("/user/**/info") public String getUserInfo() { // 获取用户信息 } ``` 上述代码中,`/user/**/info`表示匹配以`/user/`开头,并且以`/info`结尾的路径,中间可以是任意字符或者路径的组合。 通过使用URL通配符,可以让Controller的URL路径更加灵活,可以匹配不同的URL请求,并将请求参数传递给对应的方法进行处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值