使用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模板

调用接口后生成的新文件

相关推荐
HashData酷克数据6 小时前
# Apache Cloudberry 2.1.0 版本前瞻:内核、PXF 与备份生态持续演进
apache
D愿你归来仍是少年7 小时前
Apache Spark 第 8 章:Structured Streaming 流处理
大数据·spark·apache
努力的lpp1 天前
小迪安全第8天:基础入门-算法分析 & 传输加密 & 数据格式 & 密文存储 & 代码混淆 & 逆向保护
服务器·网络·apache
爱人间1 天前
apache-tomcat-8.5.72启动控制台乱码解决方案
java·tomcat·apache
XDHCOM1 天前
Apache Mesos科普,提升资源利用率与系统扩展性的开源平台
开源·apache
❀͜͡傀儡师1 天前
使用 Docker Run 部署 Apache WebDAV 服务
docker·apache·webdav
D愿你归来仍是少年1 天前
Apache Spark 详细讲解第 7 章:Shuffle 机制深度解析
大数据·spark·apache
莫爷2 天前
JSON 性能优化实战:大数据量 JSON 的处理技巧
性能优化·json·apache
D愿你归来仍是少年2 天前
Flink 并行度变更时 RocksDB 状态迁移的关键机制与原理
大数据·flink·apache
User_芊芊君子2 天前
大数据时代时序数据库选型指南:Apache IoTDB技术解析与实践建议
大数据·apache·时序数据库