Spring中iText PDF渲染网页同时存库的实现方案咨询
解决方案:一次生成PDF,同时输出响应并存储到数据库
嘿,完全不需要重复执行生成步骤!我们可以通过捕获PDF生成的字节流,同时完成两个操作:输出到HTTP响应、存入数据库。这样既高效又避免了重复渲染的开销。下面是具体实现步骤和代码示例:
核心思路
使用一个中间字节流(或者TeeOutputStream同时写入两个流)来捕获PDF的内容,这样只需要生成一次PDF,就能同时满足响应输出和数据库存储的需求。
具体代码实现
@Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // 1. 配置响应头,确保浏览器正确识别PDF response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "inline; filename=\"generated.pdf\""); // 改为attachment可触发下载 // 2. 创建字节数组输出流,用于捕获PDF内容 ByteArrayOutputStream pdfBaos = new ByteArrayOutputStream(); // 3. 使用TeeOutputStream同时写入响应输出流和字节数组流(一次生成,双输出) try (PdfWriter pdfWriter = new PdfWriter(new TeeOutputStream(pdfBaos, response.getOutputStream())); PdfDocument pdf = new PdfDocument(pdfWriter); Document doc = new Document(pdf)) { // --- 这里添加你的PDF内容生成逻辑 --- doc.add(new Paragraph("Hello, Generated PDF!")); // 示例:从model中获取业务数据填充PDF // String userContent = (String) model.get("pdfContent"); // doc.add(new Paragraph(userContent)); } // try-with-resources自动关闭资源,确保PDF写入完成 // 4. 从字节数组流中提取PDF字节数据 byte[] pdfContent = pdfBaos.toByteArray(); // 5. 将字节数组存入数据库(示例代码,根据你的持久层框架调整) // JPA示例: PdfEntity pdfEntity = new PdfEntity(); pdfEntity.setContent(pdfContent); pdfEntity.setCreatedAt(new Date()); pdfRepository.save(pdfEntity); // JDBC示例(如果不用ORM框架): // try (Connection conn = dataSource.getConnection()) { // String sql = "INSERT INTO pdf_storage (content, created_at) VALUES (?, ?)"; // try (PreparedStatement stmt = conn.prepareStatement(sql)) { // stmt.setBytes(1, pdfContent); // stmt.setTimestamp(2, new Timestamp(System.currentTimeMillis())); // stmt.executeUpdate(); // } // } }
关键细节说明
- TeeOutputStream的作用:这个类来自Apache Commons IO,如果你的项目没有依赖,也可以自己实现一个简单版本,或者先写入字节数组流再手动复制到响应流。它能让我们一次写入操作同时输出到两个流,彻底避免重复生成PDF。
- 资源关闭:必须确保
Document、PdfDocument和PdfWriter被正确关闭(用try-with-resources是最优方式),因为iText会在关闭时完成PDF的收尾工作(比如写入文件尾标记),否则流中的内容会不完整。 - 备选方案(无Apache Commons IO时):如果不想引入额外依赖,可以先写入
ByteArrayOutputStream,生成完成后再手动把字节数组写入响应输出流:// 替换步骤3的代码: try (PdfWriter pdfWriter = new PdfWriter(pdfBaos); PdfDocument pdf = new PdfDocument(pdfWriter); Document doc = new Document(pdf)) { // 生成PDF内容... } // 手动将字节数组写入响应 response.getOutputStream().write(pdfContent); response.getOutputStream().flush(); - 异常处理:实际项目中建议给数据库存储逻辑添加异常捕获,避免因为存储失败导致响应也无法正常输出(可以根据业务需求决定是否回滚响应,比如返回错误页面)。
为什么不需要重复生成?
重复生成PDF会浪费CPU和内存资源,尤其是当PDF包含大量图片、复杂表格时,性能影响会非常明显。通过捕获字节流的方式,只需要执行一次渲染逻辑,就能同时完成两个目标,是性能最优的解决方案。
内容的提问来源于stack exchange,提问作者Jay Kumo
相关产品推荐
相关产品推荐

