使用Apache的poi-ooxml 完成world模板填充

 今天公司有个新需求,说是需要把一些信息填充到world 中给用户下载打印,

作为一个crud两年半的菜鸟来说有点难度。从来没做过。

老规矩,先问下chatGpt,说是可以用Apache 的 POI完成各种office操作,

马上去官网看一下,现学,发现看不同,还是直接问chatGPT怎么操作吧

上它的代码

import org.apache.poi.xwpf.usermodel.*;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class WriteWordTemplate {
    public static void main(String[] args) {
        try {
            // 加载 Word 文档模板
            InputStream templateStream = new FileInputStream("template.docx");
            XWPFDocument document = new XWPFDocument(templateStream);

            // 获取文档中的段落和占位符(变量)并替换为实际的数据
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                replacePlaceholder(paragraph, "##NAME##", "John Doe");
            }

            // 获取文档中的表格并替换表格中的占位符(变量)为实际的数据
            for (XWPFTable table : document.getTables()) {
                for (XWPFTableRow row : table.getRows()) {
                    for (XWPFTableCell cell : row.getTableCells()) {
                        replacePlaceholder(cell, "##EMAIL##", "john.doe@example.com");
                    }
                }
            }

            // 将填充后的内容写入新的 Word 文档
            OutputStream out = new FileOutputStream("output.docx");
            document.write(out);
            out.close();

            document.close();
            System.out.println("写入成功!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void replacePlaceholder(IXWPFRun run, String placeholder, String replacement) {
        String text = run.getText(0);
        if (text != null && text.contains(placeholder)) {
            text = text.replace(placeholder, replacement);
            run.setText(text, 0);
        }
    }

    private static void replacePlaceholder(XWPFParagraph paragraph, String placeholder, String replacement) {
        for (XWPFRun run : paragraph.getRuns()) {
            replacePlaceholder(run, placeholder, replacement);
        }
    }

    private static void replacePlaceholder(XWPFTableCell cell, String placeholder, String replacement) {
        for (XWPFParagraph paragraph : cell.getParagraphs()) {
            replacePlaceholder(paragraph, placeholder, replacement);
        }
    }
}

 一看还得引包,直接去阿里镜像找

找到这个pom,直接找poi-ooxml ,因为可以看到gpt代码里用的是这个

 

<dependency>
    <groupId>;.org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
    <type>pom</type>
</dependency>

像我这样的好心人当然是直接放出来省的大家找了。

作为一个crud两年半的人当然不能像小白一样拿来直接用,简单看一下其中流程再进行优化,

可以看到第一个是针对段落的,第二个是针对表格的,但是最后都是针对getParagraphs 下的getRuns()

String text = run.getText(0);

 至于为什么始终是getText(0)

这个看了源码 

 /**
     * Return the string content of this text run
     *
     * @return the text of this text run or <code>null</code> if not set
     */
    public String getText(int pos) {
        return run.sizeOfTArray() == 0 ? null : run.getTArray(pos)
                .getStringValue();
    }
    CTText[] getTArray();

    CTText getTArray(int var1);

再往下看,看到上面挨着的有一个获取全部的,好奇心趋势下 调用了一下

发现实际上数组中一直就只有一个元素,难怪只用getText(0)

        String text = run.getText(0);
        if (text != null && text.contains(placeholder)) {
            text = text.replace(placeholder, replacement);
            run.setText(text, 0);
        }

最主要的是上面这段代码执行替换逻辑

他这里还是先把字符串替换掉,完全感觉是多余的一步,直接把需要放入的数据setText中

run.setText(StringUtil.isBlank(str) ? "": str, 0);

注意这里的字符串不能为null 否则会报错。 当然你要是头铁可以一试

我们再看这里封装

replacePlaceholder(cell, "##EMAIL##", "john.doe@example.com");

这样封装我替换一个就需要调用一次方法,实际情况下都是有很多需要替换的元素

那你的代码可能就是这样

replacePlaceholder(cell, "name", "张三");
replacePlaceholder(cell, "sex", "男");
replacePlaceholder(cell, "##EMAIL##", "john.doe@example.com");
replacePlaceholder(cell, "##EMAIL##", "john.doe@example.com");
replacePlaceholder(cell, "##EMAIL##", "john.doe@example.com");
replacePlaceholder(cell, "##EMAIL##", "john.doe@example.com");
replacePlaceholder(cell, "##EMAIL##", "john.doe@example.com");
等等等

简单一看没什么问题,但是这个封装的内部我们看了是每次都会获取其中

getParagraphs() 再 
getRuns()  进行循环

所以你每多写一个,就会多进行调用一次,浪费资源,需要优化

Map<String, String> map = EntityToMap(processDonateEntity);
			for (XWPFTable table : document.getTables()) {
				for (XWPFTableRow row : table.getRows()) {
					for (XWPFTableCell cell : row.getTableCells()) {
						replacePlaceholder(cell, map);
					}
				}
			}

可以对需要替换的信息进行map封装

private void replacePlaceholder(XWPFTableCell cell, Map<String,String> placeholder) {
		for (XWPFParagraph paragraph : cell.getParagraphs()) {
			for (XWPFRun run : paragraph.getRuns()) {
				String text = run.getText(0);
				if (text != null && placeholder.containsKey(text)) {
					run.setText(StringUtil.isBlank(placeholder.get(text)) ? "":placeholder.get(text), 0);
					break;
				}
			}
		}
	}

再去最里面循环替换时进行键值判断,进行替换,代码档次效率瞬间提升

甚至如果你的world模板中关键字是唯一,还可以在成功匹配之后加上

break; 可以减少无效的匹配,(真男人从不回头!)

这样大致就优化的差不多了。奈斯!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值