You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Spring框架,如何将Apache POI生成的XLSX转为PDF?

XLSX转PDF的可行方案(基于Spring+POI+iText7)

针对你的场景,提供两种可行的实现方案,适配现有技术栈:


方案一:基于现有XLSX Workbook转换为PDF

该方案复用你已有的Excel生成逻辑,只需在生成XSSFWorkbook后,解析内容并转换为PDF格式。

实现步骤

  1. 沿用现有逻辑生成Excel工作簿
  2. 遍历工作表、行、单元格提取数据
  3. 使用iText7构建PDF表格并填充数据
  4. 配置中文显示字体,避免乱码
  5. 将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是更高效的选择,避免中间转换的性能损耗和样式丢失问题。

实现步骤

  1. 提取原downloadExcel中的数据获取逻辑(SQL查询部分)
  2. 直接用iText7构建PDF表格、标题等元素
  3. 配置样式并填充数据
  4. 输出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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 19:27:33