今天公司有个新需求,说是需要把一些信息填充到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; 可以减少无效的匹配,(真男人从不回头!)
这样大致就优化的差不多了。奈斯!