springBoot使用XWPFDocument 和 LoopRowTableRenderPolicy 两种方式填充数据到word模版中

发布于:2025-07-10 ⋅ 阅读:(15) ⋅ 点赞:(0)

依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.3</version>
    </dependency>
    <dependency>
        <groupId>cn.afterturn</groupId>
        <artifactId>easypoi-base</artifactId>
        <version>4.4.0</version>
    </dependency>
    <dependency>
        <groupId>cn.afterturn</groupId>
        <artifactId>easypoi-word</artifactId>
        <version>4.4.0</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

controller

package com.ruoyi.web.controller.materials;

import com.ruoyi.materials.domain.wordVo.ReportData;
import com.ruoyi.materials.domain.wordVo.Student;
import com.ruoyi.materials.service.WordExportService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: yxy
 * @Date: 2025/07/09/14:22
 * @Description:
 */
@RestController
@RequestMapping("/api/word")
public class WordExportController {

    @Autowired
    private WordExportService wordExportService;

    @GetMapping("/xwpf")
    public ResponseEntity<InputStreamResource> exportByXWPFDocument() throws Exception {
        ReportData data = createSampleData();
        InputStream is = wordExportService.generateWordByXWPFDocument(data);

        return buildResponseEntity(is, "xwpf_export.docx");
    }

    @GetMapping("/looprow")
    public ResponseEntity<InputStreamResource> exportByLoopRowTable() throws Exception {
        ReportData data = createSampleData();
        InputStream is = wordExportService.generateWordByLoopRowTable(data);

        return buildResponseEntity(is, "looprow_export.docx");
    }

    private ReportData createSampleData() {
        ReportData data = new ReportData();
        data.setTitle("学生信息报告");
        data.setDate("2025年7月10日");
        data.setSchoolName("示例中学");

        List<Student> students = new ArrayList<>();
        Student student = new Student();
        student.setName("张三");
        student.setAge(18);
        Student student2 = new Student();
        student2.setName("李四");
        student2.setAge(19);
        students.add(student);
        students.add(student2);
        data.setStudents(students);

        return data;
    }

    private ResponseEntity<InputStreamResource> buildResponseEntity(InputStream is, String fileName) throws Exception {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
        return ResponseEntity.ok()
                .headers(headers)
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(new InputStreamResource(is));
    }


}

ServiceImpl

package com.ruoyi.materials.service.impl;

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.ruoyi.materials.domain.wordVo.ReportData;
import com.ruoyi.materials.domain.wordVo.Student;
import com.ruoyi.materials.service.WordExportService;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: yxy
 * @Date: 2025/07/09/14:27
 * @Description:
 */
@Service
public class WordExportServiceImpl implements WordExportService {
    
    public InputStream generateWordByXWPFDocument(ReportData data) throws IOException {
        // 加载模板
        ClassPathResource resource = new ClassPathResource("templates/xwpf_template.docx");
        try (InputStream is = resource.getInputStream();
             XWPFDocument document = new XWPFDocument(is);
             ByteArrayOutputStream out = new ByteArrayOutputStream()) {

            // 替换文本内容
            replaceText(document, "${title}", data.getTitle());
            replaceText(document, "${date}", data.getDate());
            replaceText(document, "${schoolName}", data.getSchoolName());

            // 填充表格数据
            fillTable(document, data.getStudents());

            document.write(out);
            return new ByteArrayInputStream(out.toByteArray());
        }
    }

    private void replaceText(XWPFDocument document, String placeholder, String replacement) {
        for (XWPFParagraph p : document.getParagraphs()) {
            for (XWPFRun r : p.getRuns()) {
                String text = r.getText(0);
                if (text != null && text.contains(placeholder)) {
                    text = text.replace(placeholder, replacement);
                    r.setText(text, 0);
                }
            }
        }
    }

    private void fillTable(XWPFDocument document, List<Student> students) {
        // 假设第一个表格是学生列表
        if (document.getTables().size() > 0) {
            XWPFTable table = document.getTables().get(0);

            // 从第二行开始插入数据(第一行是表头)
            for (int i = 0; i < students.size(); i++) {
                Student student = students.get(i);
                XWPFTableRow row;

                // 如果行数不够,创建新行
                if (i + 1 >= table.getNumberOfRows()) {
                    row = table.createRow();
                } else {
                    row = table.getRow(i + 1);
                }

                // 填充单元格数据
                setCellText(row, 0, student.getName());
                setCellText(row, 1, student.getAge().toString());
                setCellText(row, 2, student.getGender());
                setCellText(row, 3, student.getAddress());
            }
        }
    }

    private void setCellText(XWPFTableRow row, int cellIndex, String text) {
        XWPFTableCell cell = row.getCell(cellIndex);
        if (cell == null) {
            cell = row.addNewTableCell();
        }
        cell.setText(text);
    }



    @Override
    public InputStream generateWordByLoopRowTable(ReportData data) throws Exception {
        
        Map<String, Object> rm = new HashMap<>();
        rm.put("title",data.getTitle());
        rm.put("date",data.getDate());
        rm.put("schoolName",data.getSchoolName());
        rm.put("table", data.getStudents());
        //使用行循环插件
        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        //绑定插件所属名为list
        Configure config = Configure.builder()
                .bind("table", policy).build();
        // 调用 exportFile 方法生成文件并返回
        return exportFile(rm, config);
    }

    public InputStream exportFile(Map<String, Object> map, Configure config) throws IOException {
        // 获取模板文件的输入流
        try (InputStream ins = getClass().getResourceAsStream("/templates/looprow_template.docx")) {
            if (ins == null) {
                throw new FileNotFoundException("模板文件未找到");
            }

            // 编译模板并渲染数据
            XWPFTemplate template = XWPFTemplate.compile(ins, config).render(map);

            // 生成字节输出流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            template.write(byteArrayOutputStream);

            // 返回 ByteArrayInputStream 用于后续处理
            return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        }
    }
    

}

实体类

import lombok.Data;
import java.util.List;

@Data
public class ReportData {
    private String title;
    private String date;
    private String schoolName;
    private List<Student> students;
}

import com.ruoyi.common.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Student {
    @Excel(name = "name")
    private String name;

    @Excel(name = "age")
    private Integer age;

    @Excel(name = "gender")
    private String gender;

    @Excel(name = "address")
    private String address;

    private byte[] photo;
}

模版
xwpf_template.docx
在这里插入图片描述
looprow_template.docx
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到