使用Apache POI和POI-OOXML实现word模板文档自动填充功能

最近接到一个新的需求,用户创建好模板文件保存到模板库,然后使用在线文档编辑器打开模板时,将系统数据填充到模板文件并生成新的word文件,然后在线编辑,研究使用Apache POI和POI-OOXML实现了这个功能。

Maven依赖

XML 复制代码
        <!-- Apache POI 和 POI-OOXML -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.2</version>
        </dependency>

自动填充的接口

java 复制代码
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/fill")
public class WordFillController {

    @PostMapping("/wordFill")
    public ResponseEntity<?> wordFill() {
        String templatePath = "E:\\word\\template.docx";
        String outputPath = "E:\\word\\output.docx";

        // 待填充进模板的数据
        Map<String, String> data = new HashMap<>();
        data.put("name", "张三");
        data.put("sex", "男");
        data.put("age", "18");

        Map<String, String> data1 = new HashMap<>();
        data1.put("name", "张三1");
        data1.put("sex", "男1");
        data1.put("age", "20");

        Map<String, String> data2 = new HashMap<>();
        data2.put("name", "李四");
        data2.put("sex", "男");
        data2.put("age", "21");

        Map<String, String> data3 = new HashMap<>();
        data3.put("name", "王五");
        data3.put("sex", "女");
        data3.put("age", "45");

        List<Map<String, String>> dataList = Arrays.asList(data1, data2, data3);

        try (
                // 从模板文件创建word文档对象
                XWPFDocument doc = new XWPFDocument(Files.newInputStream(Paths.get(templatePath)));
                // 文件输出流
                FileOutputStream fos = new FileOutputStream(outputPath)
        ) {

            replacePlaceholders(doc, data);// 替换占位符
            fillTable(doc, dataList);// 填充表格
            // 将文档写入输出流
            doc.write(fos);
            // 刷新输出流
            fos.flush();

            return ResponseEntity.ok().build();
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
        }
    }

    private void replacePlaceholders(XWPFDocument doc, Map<String, String> data) {
        // 遍历文档中的段落,替换占位符
        doc.getParagraphs().forEach(paragraph -> {
            // 遍历段落中的文本,替换占位符
            paragraph.getRuns().forEach(run -> {
                // 获取文本内容
                String text = run.getText(0);
                // 判断文本是否包含占位符,如果包含,则替换占位符
                if (text != null && text.contains("${")) {
                    for (Map.Entry<String, String> entry : data.entrySet()) {
                        text = text.replace("${" + entry.getKey() + "}", entry.getValue());
                    }
                    // 设置替换后的文本
                    run.setText(text, 0);
                }
            });
        });
    }

    private void fillTable(XWPFDocument doc, List<Map<String, String>> dataList) {
        XWPFTable table = doc.getTables().get(0); // 获取第一个表格

        // 获取表格的模板行(假设表格的第一行是标题行,第二行为模板行)
        XWPFTableRow templateRow = table.getRow(1);

        // 添加新的行,并删除模板行
        for (Map<String, String> data : dataList) {
            XWPFTableRow newRow = table.createRow();

            // 手动创建每个单元格,并复制模板行的样式
            for (int i = 0; i < templateRow.getTableCells().size(); i++) {
                XWPFTableCell templateCell = templateRow.getCell(i);
                XWPFTableCell newCell = newRow.getCell(i);
                if (newCell == null) {
                    newCell = newRow.createCell();
                }

                // 复制模板行单元格的样式,不复制内容
                copyCellStyleWithoutText(templateCell, newCell);

                // 填充数据
                switch (i) {
                    case 0:
                        newCell.setText(data.get("name"));
                        break;
                    case 1:
                        newCell.setText(data.get("sex"));
                        break;
                    case 2:
                        newCell.setText(data.get("age"));
                        break;
                }
            }
        }

        // 删除模板行
        table.removeRow(1);
    }

    private void copyCellStyleWithoutText(XWPFTableCell sourceCell, XWPFTableCell targetCell) {
        // 复制单元格的样式,但不复制文本内容
        targetCell.getParagraphs().forEach(paragraph -> {
            XWPFParagraph sourceParagraph = sourceCell.getParagraphs().get(0);
            paragraph.setAlignment(sourceParagraph.getAlignment());
            paragraph.setVerticalAlignment(sourceParagraph.getVerticalAlignment());

            if (!sourceParagraph.getRuns().isEmpty()) {
                XWPFRun sourceRun = sourceParagraph.getRuns().get(0);
                XWPFRun targetRun = paragraph.createRun();

                targetRun.setBold(sourceRun.isBold());
                targetRun.setItalic(sourceRun.isItalic());
                targetRun.setFontFamily(sourceRun.getFontFamily());
                targetRun.setFontSize(sourceRun.getFontSize());
            }
        });

        // 复制单元格背景色等样式
        targetCell.setColor(sourceCell.getColor());
        targetCell.setVerticalAlignment(sourceCell.getVerticalAlignment());
    }
}

word模板

调用接口后生成的新文件

相关推荐
花千树-01018 小时前
Java中的自然语言处理(NLP)工具:Stanford NLP、Apache OpenNLP、DL4J
java·自然语言处理·nlp·aigc·apache
Bai_Yin1 天前
Debezium 与 Apache Kafka 的集成方式
分布式·kafka·apache·debezium
xing.yu.CTF1 天前
Web入侵实战分析-常见web攻击类应急处置实验2
运维·服务器·windows·web安全·apache·php漏洞·phpstudy后门漏洞
10km1 天前
java:Apache Commons Configuration2占位符解析异常的正确解法:${prefix:name:-default}
java·apache·configuration2·变量插值·interpolation
Apache IoTDB3 天前
Apache IoTDB v2.0.1-beta 发布|树、表双模型支持,更灵活更全面!
apache·iotdb
垚垚 Securify 前沿站3 天前
Apache Logic4j 库反序列化漏洞复现与深度剖析
linux·网络·安全·web安全·系统安全·apache
undo_try3 天前
大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1)
大数据·flink·apache
花千树-0104 天前
使用 Apache PDFBox 提取 PDF 中的文本和图像
java·pdf·apache·ai编程
百事可乐☆5 天前
uniapp 支付宝小程序自定义导航栏
小程序·uni-app·apache
虎鲸不是鱼6 天前
Win10环境借助DockerDesktop部署大数据时序数据库Apache Druid
大数据·docker·apache·时序数据库·druid