Java HTML转PDF的两种实现方案

1.Flying Saucer

项目github地址:https://github.com/flyingsaucerproject/flyingsaucer

license: LGPL

pom依赖

 <!-- Flying Saucer 核心 -->
<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-core</artifactId>
    <version>9.1.22</version>
</dependency>
<!-- Flying Saucer PDF 支持(使用 OpenPDF) -->
<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf-openpdf</artifactId>
    <version>9.1.22</version>
</dependency>

实现demo

public byte[] htmlToPdf(String htmlContent) {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        ITextRenderer renderer = new ITextRenderer();
        renderer.getFontResolver().addFont("C:/Windows/Fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        // 设置文档
        renderer.setDocumentFromString(htmlContent);
        renderer.layout();
        renderer.createPDF(baos);
        return baos.toByteArray();
    } catch (Exception e) {
        return new byte[0];
    }
}

20kb的html文件转为pdf内存占用

image

这张图为项目初始化时,各个线程申请内存的情况。

image-20251118232434468

这张图为调用了两次生成pdf方法时,各个线程申请内存的情况。

exec-3和exec-4为具体执行生成pdf方法的线程,大概测算出每次调用会占用 24997744 / 1024 /1024 = 23.8 M内存 (这是目前能想到的最好的测试方法执行时内存占用的方法)

2.openhtmltopdf

项目github地址:https://github.com/danfickle/openhtmltopdf

license: LGPL

pom依赖

<dependency>
    <groupId>com.openhtmltopdf</groupId>
    <artifactId>openhtmltopdf-pdfbox</artifactId>
    <version>1.0.10</version>
</dependency>

实现demo

public byte[] htmlToPdf2(String htmlContent) {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        PdfRendererBuilder builder = new PdfRendererBuilder();
        builder.withHtmlContent(htmlContent, null);
        // 确认字体路径有效
        File fontFile = new File("C:/Windows/Fonts/simsun.ttc");
        if (!fontFile.exists()) {
            System.err.println("Font file not found!");
        } else {
            builder.useFont(fontFile, "SimSun");
        }
        builder.toStream(baos);
        builder.run();
        return baos.toByteArray();
    } catch (Exception e) {
        return new byte[0];
    }
}

20kb的html文件转为pdf内存占用

image-20251118233615921

image-20251118233641194

每次调用会占用 37105968/ 1024 /1024 = 35.3 M内存

3.实际应用

通过上面的内存占用测试,不难看出每次调用对内存的需求还是比较大的,如果并发较高很容易造成频繁GC甚至OOM

所以推荐根据部署的机器的实际内存情况使用线程池来达到限流的目的

线程池核心参数配置思路:

假设实际测试下来,单次转换时间为 t, 方法执行时消耗的内存为 preAllocate,能够接受并发执行时最大占用的内存为totalAllocate,部署的机器集群数量为n,那具体的Tps计算公式为:

\[TPS = n × \frac{\frac{totalAllocate}{preAllocate}}{t} \]

假设cpu核心数为cpuCoreCount,线程池核心参数配置:

  • 最大线程数:min(totalAllocate / preAllocate, cpuCoreCount)
  • 核心线程数:小于最大线程数(视业务而定)
  • 队列类型:有界队列
  • 队列容量:业务能容忍的最大等待时间 * 单机TPS
  • 拒绝策略:可以自定义将任务记录(可选择持久化,在业务低峰期重试)后丢弃
posted @ 2025-11-19 00:32  人之为言  阅读(46)  评论(0)    收藏  举报