依赖
<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