springBoot使用XWPFDocument 和 LoopRowTableRenderPolicy 两种方式填充数据到word模版中
依赖
<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 {@Autowiredprivate 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);}@Overridepublic 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();//绑定插件所属名为listConfigure 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