SpringBoot - poi-tl合并word文件表格,复杂word表格

简介:

        上篇文章说到开发中我们需要通过在word中使用占位符来动态渲染一些数据,

        原文链接SpringBoot poi-tl通过模板占位符生成word文件-CSDN博客

        本文讲解poi-tl实现word表格之合并表格。


1. word格式
2. 导出后的docx文件 样式

3. 依赖

上篇文章有说过

SpringBoot poi-tl通过模板占位符生成word文件-CSDN博客

4. 实现代码

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;

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

public class EditWordUtil {
    public static void main(String[] args) {
        String filePath = "D:/biao4.docx";

        try (FileInputStream resourceAsStream = new FileInputStream(new File(filePath));) {

            Map<String, Object> map = new HashMap<>();

            ArrayList<Map<String, Object>> dataList = new ArrayList<>();

            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 2; j++) {
                    HashMap<String, Object> maps = new HashMap<>();
                    maps.put("name", "龙龙" + (i + 1));
                    maps.put("skill", "Java" + j);
                    maps.put("lv", (j + 1) + "级");
                    maps.put("remak", "无");
                    dataList.add(maps);
                }
            }
            map.put("list", dataList);
            // 调用生成 Word 文件方法,将结果保存到本地
            EditWordUtil.mergeTablesFun("D:/out.docx", resourceAsStream, map, 0, 0);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

  
    /**
     * 导出有合并表格的word
     *
     * @param outputPath
     * @param templatePath
     * @param data
     * @param pos              表格在文档的第几个表中,获取表格 从0开始
     * @param mergeColumnIndex 合并 相同的数据 第几列的单元格 从0开始
     * @return
     * @throws IOException
     */
    public static boolean mergeTablesFun(String outputPath, InputStream templatePath, Map<String, Object> data,
                                         int pos, int mergeColumnIndex

    ) throws IOException {
        try (FileOutputStream out = new FileOutputStream(outputPath);
             BufferedOutputStream bos = new BufferedOutputStream(out)) {

            ConfigureBuilder builder = Configure.builder();
            LoopRowTableRenderPolicy loopRowTableRenderPolicy = new LoopRowTableRenderPolicy();

            handleEmptyKeys(data);

            // 动态绑定
            for (Map.Entry<String, Object> entry : data.entrySet()) {
                if (entry.getValue() instanceof ArrayList) {
                    builder.bind(entry.getKey(), loopRowTableRenderPolicy);

                    // 如果是嵌套列表,递归绑定
                    bindNestedLists(entry.getKey(), entry.getValue(), builder);
                }
            }

            Configure configure = builder.build();

            // 读取模板并渲染数据
            XWPFTemplate template = XWPFTemplate.compile(templatePath, configure).render(data);

            // 获取文档对象
            XWPFDocument document = template.getXWPFDocument();

            // 表格在文档的第pos个表中,获取表格
            XWPFTable table = document.getTableArray(pos);

            // 合并 相同的数据 第mergeColumnIndex列的单元格
            mergeCellsWithSameValue(table, mergeColumnIndex);

            try {
                template.write(bos);
                template.close();
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }

            out.flush();
            bos.flush();
        }
        return true;
    }

    // 递归绑定嵌套列表
    private static void bindNestedLists(String key, Object value, ConfigureBuilder builder) {
        if (value instanceof ArrayList) {
            for (Object item : (ArrayList<?>) value) {
                if (item instanceof Map) {
                    for (Map.Entry<String, Object> nestedEntry : ((Map<String, Object>) item).entrySet()) {
                        if (nestedEntry.getValue() instanceof ArrayList) {
                            // 绑定嵌套列表
                            builder.bind(nestedEntry.getKey(), new LoopRowTableRenderPolicy());
                            bindNestedLists(nestedEntry.getKey(), nestedEntry.getValue(), builder);
                        }
                    }
                }
            }
        }
    }


    // 递归检查map的key是否为空, 为空返回 ---
    public static void handleEmptyKeys(Map<String, Object> map) {
        // 遍历 Map 中的所有键值对
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();

            // 如果键为空或者键值为null
            if (key == null || key.isEmpty()) {
                map.put(key, "---");
            }

            // 如果值是 Map 类型,递归调用
            if (value instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> nestedMap = (Map<String, Object>) value;

                // 递归检查嵌套的 Map
                handleEmptyKeys(nestedMap);

                // 如果嵌套Map为空, 也设置为 "---"
                if (nestedMap.isEmpty()) {
                    map.put(key, "---");
                }
            }

            // 如果值为空
            else if (value == null) {
                map.put(key, "---");
            }
        }
    }


    // 合并相同值的单元格,colIndex 是要合并的列索引
    private static void mergeCellsWithSameValue(XWPFTable table, int colIndex) {
        String previousValue = null;
        int startRow = -1;
        int rowCount = table.getNumberOfRows();

        for (int i = 0; i < rowCount; i++) {
            XWPFTableRow row = table.getRow(i);
            XWPFTableCell cell = row.getCell(colIndex);
            String cellValue = cell.getText();

            // 如果当前值与前一个值相同,继续合并
            if (cellValue.equals(previousValue)) {
                continue;
            }

            // 如果前一个值不为空,且有多行需要合并,执行合并
            if (previousValue != null && i > startRow + 1) {
                mergeCellsVertically(table, colIndex, startRow, i - 1);
            }

            // 更新为当前值,并记录新起始行
            previousValue = cellValue;
            startRow = i;
        }

        // 处理最后一组相同值的合并
        if (startRow < rowCount - 1) {
            mergeCellsVertically(table, colIndex, startRow, rowCount - 1);
        }
    }

    // 合并表格垂直方向的单元格
    private static void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
        for (int i = fromRow; i <= toRow; i++) {
            XWPFTableCell cell = table.getRow(i).getCell(col);
            CTVMerge vMerge = CTVMerge.Factory.newInstance();
            if (i == fromRow) {
                // 第一个合并的单元格设置为 RESTART
                vMerge.setVal(STMerge.RESTART);
            } else {
                // 其他单元格设置为 CONTINUE
                vMerge.setVal(STMerge.CONTINUE);
            }
            cell.getCTTc().getTcPr().setVMerge(vMerge);
        }
    }
}


结语

🔥如果文章对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下小老弟,蟹蟹大咖们~ 

Spring Boot中使用poi-tl库来导出带有合并列的Word表格并下载,您可以按照以下步骤操作: 1. 首先,确保您的Spring Boot项目中已经添加了poi-tl的依赖。您可以在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.6.0</version> </dependency> ``` 2. 创建一个Controller来处理导出请求。例如,创建一个名为WordExportController的类,并添加一个处理导出请求的方法。 ```java import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.data.*; import com.deepoove.poi.util.BytePictureUtils; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; @Controller public class WordExportController { @GetMapping("/export") public ResponseEntity<InputStreamResource> exportWord() throws IOException { // 创建一个数据模型 List<List<String>> tableData = new ArrayList<>(); tableData.add(createRow("Merged Cells", "Cell 3")); tableData.add(createRow("Cell 4", "Cell 6")); // 使用poi-tl的XWPFTemplate来生成Word文档 XWPFTemplate template = XWPFTemplate.compile("templates/template.docx").render( new DataTable(tableData) .setHeader(createRow("Header 1", "Header 2")) .setCellWidth(2000) // 设置单元格宽度 .setHeaderCellStyle(new CellStyle().setBold(true).setColor("FFFFFF").setBgColor("336699")) .setOddRowCellStyle(new CellStyle().setColor("FFFFFF").setBgColor("99CCFF")) .setEvenRowCellStyle(new CellStyle().setColor("FFFFFF").setBgColor("CCEEFF")) ); // 将生成的Word文档转换为字节数组 ByteArrayOutputStream out = new ByteArrayOutputStream(); template.write(out); byte[] documentBytes = out.toByteArray(); // 设置下载响应的头信息 HttpHeaders headers = new HttpHeaders(); headers.setContentDispositionFormData("attachment", "merged_table.docx"); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); // 创建一个包含Word文档字节数组的InputStreamResource InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(documentBytes)); // 返回响应实体 return ResponseEntity.ok() .headers(headers) .body(resource); } private List<String> createRow(String cell1, String cell2) { List<String> row = new ArrayList<>(); row.add(cell1); row.add(cell2); return row; } } ``` 3. 在resources目录下创建一个名为template.docx的Word模板文件。在模板文件中,您可以根据自己的需求设置表格样式和内容。 4. 启动您的Spring Boot应用程序,并访问导出请求的URL(例如:http://localhost:8080/export)。将会自动下载名为merged_table.docx的Word文档,其中包含合并列的表格。 请确保按照您的需求修改代码,并根据模板文件的位置进行相应的调整。 希望对您有所帮助!如果您有任何其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值