SpringBoot操作XWPFTable(2)创建行和列

本文介绍了在使用ApachePOI库处理Word文档时,如何动态创建和修改表格,包括createRow、insertNewTableRow方法的使用及其限制,以及处理样式和数据清除的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文链接:https://www.longkui.site/program/java/xwpftable2/5648/

0.前言
上一篇文章介绍了创建表格和读取表格,在很多时候,我们需要动态的创建表格和修改表格内容。这篇文章介绍一些哪些创建行和列的方式。比较常见的创建行的方式有

createRow()
addRow()
insertNewTableRow()
addNewRowBetween()
官方文档:https://poi.apache.org/apidocs/4.0/org/apache/poi/xwpf/usermodel/XWPFTable.html

1.创建行
addNewRowBetween(int start, int end)
在某个区间范围内插入行,但是官方显示此方法已经弃用,所以不推荐

createRow()
创建一个新的XWPFTableRow对象,其单元格数与该时刻定义的列数一样多。

XWPFTableRow row = table.createRow();
参考代码:

@Test
void testXWPFTable1(){
System.out.println(“开始执行”);
try {
//定义word
XWPFDocument doc = new XWPFDocument();
//在word中创建一个表格(2行,3列)
XWPFTable table = doc.createTable(2,3);
//第一行数据
table.getRow(0).getCell(0).setText(“1”);
table.getRow(0).getCell(1).setText(“2”);
table.getRow(0).getCell(2).setText(“3”);
//插入新的一行
XWPFTableRow row = table.createRow();

        table.getRow(1).getCell(0).setText("4");
        table.getRow(1).getCell(1).setText("5");
        table.getRow(1).getCell(2).setText("666666");
        //导出
        String path = "C:\\";  //文件路径
        String name = "test1";  //文件名
        path = path + "/" + name + ".docx";
        File file = new File(path);
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream out = new FileOutputStream(file);
        doc.write(out);
    }catch (IOException  e){
        e.printStackTrace();
    }
}

效果:

可以看出,createRow方法是在表格底部创建一行。另外我们在单元格中写入数据“666666”然后可以看到,我们创建的表格会自适应宽度。

实际遇到的问题:

在另一个springMVC项目中,使用createRow方法创建的行是没有边框没有任何样式的。

insertNewTableRow(int pos)
在pos位置插入一行数据,使用insertNewTableRow创建行,必须填充列,且列数必须统一,否则word会报错:“word在试图打开文件时遇到错误。请尝试下列法方法:检查文档或驱动器的文件权限。确保有足够的内存和磁盘空间。用文本恢复转换器打开文件。”

void testXWPFTable1(){
    System.out.println("开始执行");
    try {
        //定义word
        XWPFDocument doc = new XWPFDocument();
        //在word中创建一个表格(2行,3列)
        XWPFTable table = doc.createTable(2,3);
        //第一行数据
        table.getRow(0).getCell(0).setText("1");
        table.getRow(0).getCell(1).setText("2");
        table.getRow(0).getCell(2).setText("3");
        //插入新的一行
        XWPFTableRow row = table.insertNewTableRow(1);
        //使用insertNewTableRow必须填充列,且列数必须统一
        for (int j = 0; j < 3; j++) {
            row.createCell(); //创建列
        }
        //同时,表格行和列的数量变了,所以这个地方从2开始
        table.getRow(2).getCell(0).setText("4");
        table.getRow(2).getCell(1).setText("5");
        table.getRow(2).getCell(2).setText("666666");
        //导出
        String path = "C:\\";  //文件路径
        String name = "test1";  //文件名
        path = path + "/" + name + ".docx";
        File file = new File(path);
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream out = new FileOutputStream(file);
        doc.write(out);
    }catch (IOException  e){
        e.printStackTrace();
    }
}

效果:

可以看出,可以在中间创建一行。

实际问题:在另一个项目中使用此方法,创建出来的行没有边框没有样式。

addRow((XWPFTableRow copyRow, int pos)
按照copyRow的样式,在pos位置插入新的一行,新的一行具有copyRow的样式。

错误用法1:直接复制某一行数据

void testXWPFTable1(){
System.out.println(“开始执行”);
try {
//定义word
XWPFDocument doc = new XWPFDocument();
//在word中创建一个表格(2行,3列)
XWPFTable table = doc.createTable(2,3);
//第一行数据
table.getRow(0).getCell(0).setText(“1”);
table.getRow(0).getCell(1).setText(“2”);
table.getRow(0).getCell(2).setText(“3”);
//这样会完全克隆上 row=0 行数据,连数据都一样
table.addRow(table.getRow(0),1);
//开始修改新创建的行的数据
table.getRow(1).getCell(0).setText(“99”);
table.getRow(1).getCell(1).setText(“88”);
table.getRow(1).getCell(2).setText(“77”);

        //同时,表格行和列的数量变了
        table.getRow(2).getCell(0).setText("4");
        table.getRow(2).getCell(1).setText("5");
        table.getRow(2).getCell(2).setText("666666");
        //导出
        String path = "C:\\";  //文件路径
        String name = "test1";  //文件名
        path = path + "/" + name + ".docx";
        File file = new File(path);
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream out = new FileOutputStream(file);
        doc.write(out);
    }catch (IOException  e){
        e.printStackTrace();
    }
}

如果我们直接通过getRow作为复制行,然后修改行数据,实际上修改的是被复制行的数据。

效果如下:

可以看出,原来表头是 1 2 3 那行数据,我们复制那行数据,然后修改,发现表头被修改了,然后新复制出来的那行没有被修改。

比较推荐的复制行方法是:

CTRow ctrow = CTRow.Factory.parse(workbook.getRow(12).getCtRow().newInputStream());  //复制一行

错误用法2:操作后再插入数据会导致修改内容失败

比如下面这样:

void testXWPFTable1(){
System.out.println(“开始执行”);
try {
//定义word
XWPFDocument doc = new XWPFDocument();
//在word中创建一个表格(2行,3列)
XWPFTable table = doc.createTable(2,3);
//第一行数据
table.getRow(0).getCell(0).setText(“1”);
table.getRow(0).getCell(1).setText(“2”);
table.getRow(0).getCell(2).setText(“3”);

        CTRow ctrow = null;
        try {
            ctrow = CTRow.Factory.parse(table.getRow(0).getCtRow().newInputStream());
        } catch (XmlException e) {
            e.printStackTrace();
        }
        XWPFTableRow copyRow = new XWPFTableRow(ctrow, table);

        table.addRow(copyRow,1);
        //开始修改新创建的行的数据
        table.getRow(1).getCell(0).setText("99");
        table.getRow(1).getCell(1).setText("88");
        table.getRow(1).getCell(2).setText("77");

        //同时,表格行和列的数量变了
        table.getRow(2).getCell(0).setText("4");
        table.getRow(2).getCell(1).setText("5");
        table.getRow(2).getCell(2).setText("666666");
        //导出
        String path = "C:\\";  //文件路径
        String name = "test1";  //文件名
        path = path + "/" + name + ".docx";
        File file = new File(path);
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream out = new FileOutputStream(file);
        doc.write(out);
    }catch (IOException  e){
        e.printStackTrace();
    }
}

效果:

可以看出新插入的行的数据即使在代码中设置数据也没有生效。

正确用法:先复制再设置内容最后插入

void testXWPFTable1(){
System.out.println(“开始执行”);
try {
//定义word
XWPFDocument doc = new XWPFDocument();
//在word中创建一个表格(2行,3列)
XWPFTable table = doc.createTable(2,3);
//第一行数据
table.getRow(0).getCell(0).setText(“1”);
table.getRow(0).getCell(1).setText(“2”);
table.getRow(0).getCell(2).setText(“3”);

        CTRow ctrow = null;
        try {
            ctrow = CTRow.Factory.parse(table.getRow(0).getCtRow().newInputStream());
        } catch (XmlException e) {
            e.printStackTrace();
        }
        XWPFTableRow copyRow = new XWPFTableRow(ctrow, table);
        //设置内容
        copyRow.getCell(0).setText("99");
        copyRow.getCell(1).setText("88");
        copyRow.getCell(2).setText("77");
        //插入
        table.addRow(copyRow,1);
        
        //同时,表格行和列的数量变了
        table.getRow(2).getCell(0).setText("4");
        table.getRow(2).getCell(1).setText("5");
        table.getRow(2).getCell(2).setText("666666");
        //导出
        String path = "C:\\";  //文件路径
        String name = "test1";  //文件名
        path = path + "/" + name + ".docx";
        File file = new File(path);
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream out = new FileOutputStream(file);
        doc.write(out);
    }catch (IOException  e){
        e.printStackTrace();
    }
}

效果:

2.问题
除了上面说到的遇到的实际问题,还遇到了其他问题,比如我们复制行数据后,使用setText方法对新行设置数据,发现原来的数据没有被清除,而是在同一个单元格进行了追加。(实际操作中,发现在代码中创建的新表格没有这个问题,而在代码中读取word中预创建的表格会有这个问题)

这样的话上面的表格会变成

此时我们可以用下面的代码清除某个单元格的数据

copyRow.getTableCells().get(j).getCTTc().setPArray(new CTP[] {CTP.Factory.newInstance()}); //清除单元格信息
其中上面的代码中的j表示某个列。

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文档,其中包含合并的表格。 请确保按照您的需求修改代码,并根据模板文件的位置进相应的调整。 希望对您有所帮助!如果您有任何其他问题,请随时提问。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值