PDF 与 Excel
Spring MVC 提供了内置支持来生成动态的 PDF 文件和 Excel 报表。这在生成账单、统计报告或数据导出场景中非常有用。由于 PDF 和 Excel 属于二进制文档,Spring 使用特殊的抽象视图类来处理这类渲染。
PDF 视图
Spring 的 PDF 支持基于 iText 2.1.7 或 OpenPDF。
实现步骤
- 创建视图类:继承
AbstractPdfView。 - 实现
buildPdfDocument:在该方法中操作 PDF 对象。
java
public class UserPdfView extends AbstractPdfView {
@Override
protected void buildPdfDocument(Map<String, Object> model, Document document,
PdfWriter writer, HttpServletRequest request, HttpServletResponse response) {
List<User> users = (List<User>) model.get("users");
document.add(new Paragraph("用户列表说明"));
Table table = new Table(2);
table.addCell("用户名");
table.addCell("邮箱");
for (User user : users) {
table.addCell(user.getName());
table.addCell(user.getEmail());
}
document.add(table);
}
}kotlin
class UserPdfView : AbstractPdfView() {
override fun buildPdfDocument(model: Map<String, Any>, document: Document,
writer: PdfWriter, request: HttpServletRequest,
response: HttpServletResponse) {
val users = model["users"] as List<User>
document.add(Paragraph("用户列表说明"))
val table = Table(2).apply {
addCell("用户名")
addCell("邮箱")
}
users.forEach { user ->
table.addCell(user.name)
table.addCell(user.email)
}
document.add(table)
}
}Excel 视图
Spring 的 Excel 支持基于 Apache POI 库。
视图类版本
- AbstractXlsView: 使用经典的
.xls格式(Excel 97-2003)。 - AbstractXlsxView: 使用现代的
.xlsx格式(推荐)。 - AbstractXlsxStreamingView: 用于大数据量的流式导出,内存占用极低。
示例代码
java
public class UserExcelView extends AbstractXlsxView {
@Override
protected void buildExcelDocument(Map<String, Object> model, Workbook workbook,
HttpServletRequest request, HttpServletResponse response) {
Sheet sheet = workbook.createSheet("用户数据");
Row header = sheet.createRow(0);
header.createCell(0).setCellValue("ID");
header.createCell(1).setCellValue("用户名");
List<User> users = (List<User>) model.get("users");
int rowNum = 1;
for (User user : users) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(user.getId());
row.createCell(1).setCellValue(user.getName());
}
}
}kotlin
class UserExcelView : AbstractXlsxView() {
override fun buildExcelDocument(model: Map<String, Any>, workbook: Workbook,
request: HttpServletRequest,
response: HttpServletResponse) {
val sheet = workbook.createSheet("用户数据")
val header = sheet.createRow(0)
header.createCell(0).setCellValue("ID")
header.createCell(1).setCellValue("用户名")
val users = model["users"] as List<User>
users.forEachIndexed { index, user ->
val row = sheet.createRow(index + 1)
row.createCell(0).setCellValue(user.id.toDouble())
row.createCell(1).setCellValue(user.name)
}
}
}控制器用法
在控制器中,你只需要返回该视图的实例即可。
java
@GetMapping("/download/users/excel")
public ModelAndView downloadExcel() {
List<User> users = userService.findAll();
return new ModelAndView(new UserExcelView(), "users", users);
}补充教学
1. 为什么使用 AbstractView 而不是直接操作 Response?
虽然你可以直接在控制器方法中通过 HttpServletResponse.getOutputStream() 写入数据,但继承 AbstractView 有以下好处:
- 关注点分离:控制器只负责业务数据,视图只负责转换格式。
- 内容协商:可以轻松集成到 Spring 的内容协商机制中,通过不同的扩展名(
.pdf/.xlsx)分发到不同的视图。 - 复用性:一个视图类可以被多个控制器复用。
2. 响应头设置
通常,下载文件时需要设置 Content-Disposition 为 attachment 才能弹出保存对话框。 在视图类中,你可以通过覆盖 renderMergedOutputModel 或直接在核心方法中使用 response 对象:
java
response.setHeader("Content-Disposition", "attachment; filename=\"report.xlsx\"");3. POI 与 内存管理
在生成超大规模 Excel(如 10 万行以上)时,务必使用 AbstractXlsxStreamingView。它对应 POI 的 SXSSF 实现,会将临时行刷入磁盘,避免 OutOfMemoryError。