基于Spring框架,如何将Apache POI生成的XLSX转为PDF?
XLSX转PDF的可行方案(基于Spring+POI+iText7)
针对你的场景,提供两种可行的实现方案,适配现有技术栈:
方案一:基于现有XLSX Workbook转换为PDF
该方案复用你已有的Excel生成逻辑,只需在生成XSSFWorkbook后,解析内容并转换为PDF格式。
实现步骤
- 沿用现有逻辑生成Excel工作簿
- 遍历工作表、行、单元格提取数据
- 使用iText7构建PDF表格并填充数据
- 配置中文显示字体,避免乱码
- 将PDF输出到响应流
代码示例(修改原有下载接口)
@RequestMapping(value = "/downloadPdf", method = RequestMethod.POST) @PreAuthorize("hasAuthority('CTRL_REPORT_READ')") @ResponseBody public void downloadPdf(@Valid @ModelAttribute ReportForm reportForm, BindingResult result, RedirectAttributes redirectAttrs, HttpServletRequest request, HttpServletResponse response) throws IOException { logger.debug("IN: reimbursement/download report-PDF POST"); XSSFWorkbook workbook = null; PdfWriter writer = null; PdfDocument pdfDoc = null; Document document = null; try { // 1. 复用现有逻辑生成Excel Workbook workbook = new XSSFWorkbook(); downloadExcel(reportForm, workbook); XSSFSheet sheet = workbook.getSheetAt(0); // 2. 初始化PDF文档 response.setContentType("application/pdf"); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); String fileName = "Reimbursement Report " + sdf.format(new Date()) + ".pdf"; // 编码文件名避免中文乱码 response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8")); writer = new PdfWriter(response.getOutputStream()); pdfDoc = new PdfDocument(writer); // 横向布局适配表格 document = new Document(pdfDoc, PageSize.A4.rotate()); document.setMargins(20, 20, 20, 20); // 3. 配置中文字体 PdfFont font = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); // 4. 构建PDF表格 int columnCount = sheet.getRow(0).getPhysicalNumberOfCells(); Table table = new Table(UnitValue.createPercentArray(columnCount)).useAllAvailableWidth(); // 遍历Excel行填充表格 for (Row row : sheet) { for (Cell cell : row) { String cellValue = ""; switch (cell.getCellTypeEnum()) { case STRING: cellValue = cell.getStringCellValue(); break; case NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { cellValue = new SimpleDateFormat("yyyy-MM-dd").format(cell.getDateCellValue()); } else { cellValue = String.valueOf(cell.getNumericCellValue()); } break; case BOOLEAN: cellValue = String.valueOf(cell.getBooleanCellValue()); break; default: cellValue = ""; } Cell pdfCell = new Cell().add(new Paragraph(cellValue).setFont(font)); table.addCell(pdfCell); } } // 5. 写入PDF文档 document.add(table); } catch (Exception e) { logger.error("Error generating PDF report: " + e.getMessage()); redirectAttrs.addFlashAttribute(AppDataConstant.ERROR_FLASH_RESP, "Download reimbursement PDF report failed"); } finally { // 资源关闭顺序 if (document != null) document.close(); if (pdfDoc != null) pdfDoc.close(); if (writer != null) writer.close(); if (workbook != null) workbook.close(); } }
方案二:直接从数据源生成PDF(性能更优)
如果不需要保留XLSX版本,直接从数据库取数后用iText7生成PDF是更高效的选择,避免中间转换的性能损耗和样式丢失问题。
实现步骤
- 提取原
downloadExcel中的数据获取逻辑(SQL查询部分) - 直接用iText7构建PDF表格、标题等元素
- 配置样式并填充数据
- 输出PDF到响应流
代码示例
@RequestMapping(value = "/downloadPdfDirect", method = RequestMethod.POST) @PreAuthorize("hasAuthority('CTRL_REPORT_READ')") @ResponseBody public void downloadPdfDirect(@Valid @ModelAttribute ReportForm reportForm, BindingResult result, RedirectAttributes redirectAttrs, HttpServletRequest request, HttpServletResponse response) throws IOException { logger.debug("IN: reimbursement/download report-PDF Direct POST"); PdfWriter writer = null; PdfDocument pdfDoc = null; Document document = null; try { // 1. 获取报表数据(复用原Excel生成逻辑中的取数代码) List<ReimbursementDTO> reportData = getReimbursementReportData(reportForm); List<String> headers = Arrays.asList("序号", "报销单号", "员工姓名", "报销金额", "报销日期", "状态"); // 2. 初始化PDF文档 response.setContentType("application/pdf"); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); String fileName = "Reimbursement Report " + sdf.format(new Date()) + ".pdf"; response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8")); writer = new PdfWriter(response.getOutputStream()); pdfDoc = new PdfDocument(writer); document = new Document(pdfDoc, PageSize.A4.rotate()); document.setMargins(20, 20, 20, 20); // 3. 配置字体 PdfFont titleFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); PdfFont contentFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); // 4. 添加报表标题 Paragraph title = new Paragraph("报销明细报表") .setFont(titleFont) .setFontSize(16) .setTextAlignment(TextAlignment.CENTER) .setMarginBottom(20); document.add(title); // 5. 构建PDF表格 Table table = new Table(UnitValue.createPercentArray(headers.size())).useAllAvailableWidth(); // 添加表头 for (String header : headers) { Cell headerCell = new Cell().add(new Paragraph(header).setFont(contentFont).setBold()); table.addCell(headerCell); } // 添加数据行 int index = 1; for (ReimbursementDTO dto : reportData) { table.addCell(new Cell().add(new Paragraph(String.valueOf(index++)).setFont(contentFont))); table.addCell(new Cell().add(new Paragraph(dto.getReimburseNo()).setFont(contentFont))); table.addCell(new Cell().add(new Paragraph(dto.getEmpName()).setFont(contentFont))); table.addCell(new Cell().add(new Paragraph(String.valueOf(dto.getAmount())).setFont(contentFont))); table.addCell(new Cell().add(new Paragraph(new SimpleDateFormat("yyyy-MM-dd").format(dto.getReimburseDate())).setFont(contentFont))); table.addCell(new Cell().add(new Paragraph(dto.getStatus()).setFont(contentFont))); } // 6. 写入PDF文档 document.add(table); } catch (Exception e) { logger.error("Error generating direct PDF report: " + e.getMessage()); redirectAttrs.addFlashAttribute(AppDataConstant.ERROR_FLASH_RESP, "Download reimbursement PDF report failed"); } finally { if (document != null) document.close(); if (pdfDoc != null) pdfDoc.close(); if (writer != null) writer.close(); } }
关键注意事项
- 中文乱码处理:必须依赖已引入的
font-asian包提供的中文字体,或嵌入自定义字体文件 - 样式兼容:Excel中的复杂样式(合并单元格、背景色、公式)转换到PDF需要额外处理,直接生成PDF时样式可控性更强
- 版本兼容:你当前使用的POI 3.15与iText7 7.2.3版本兼容无冲突
- 性能优化:数据量大时,直接生成PDF的性能优于先生成Excel再转换,可降低内存占用
内容的提问来源于stack exchange,提问作者Ikhsan Kurnia Prayoga
相关产品推荐
相关产品推荐

