Java使用Apache PDFBox进行PDF转图片体积过大的优化指南

 更新时间:2026年03月05日 08:45:55   作者:一勺菠萝丶  
在项目中,我们有一个接口用于将 PDF 按页拆分为图片,方便前端逐页展示,上线一段时间后发现体积过大了,下面我们就来看看如何进行简单的优化吧

一、背景

在项目中,我们有一个接口用于将 PDF 按页拆分为图片,方便前端逐页展示。

原始实现逻辑:

  • 使用 Apache PDFBoxPDFRenderer
  • 调用 renderImageWithDPI
  • 使用 ImageIO.write(image, "PNG", file) 输出 PNG
  • 每一页生成一张大图,路径存入数据库

上线一段时间后发现:

  • 服务器磁盘增长很快
  • 图片加载明显变慢
  • 图片接口内存压力升高
  • 单页图片体积动辄几 MB

于是开始排查问题。

二、问题分析

造成图片体积过大的核心原因有 3 个:

DPI 过高

原始代码:

renderer.renderImageWithDPI(i, 144);

144 DPI 会生成较高分辨率图片,页面尺寸较大。

DPI 越高,生成的图片分辨率越大,文件体积呈指数级增长。

不做尺寸限制

直接按 PDF 原始尺寸渲染输出。

例如:

  • A4 页面在 144 DPI 下
  • 宽度可能接近 2000+ 像素

对于仅用于网页展示来说完全没有必要。

使用 PNG 无损格式

PNG 是无损压缩,适合:

  • 图标
  • 透明图片
  • 插画

但对于:

  • 含大量文字 + 图片的课件 PDF
  • 整页截图型内容

PNG 文件体积会非常大。

总结一句话

高 DPI + 原始大尺寸 + PNG 无损 = 单页图片非常大

三、优化目标

在保证肉眼清晰可读的前提下:

  • 降低图片体积
  • 降低磁盘增长速度
  • 减少网络传输时间
  • 降低内存压力

并且:

  • 不改接口签名
  • 不改 Controller
  • 只在工具类内部优化

四、整体优化方案

优化策略:

  • 降低 DPI
  • 限制最大宽度,等比例缩放
  • 保持 PNG(兼容现有业务)

五、最终实现代码(核心优化版)

public class PdfToImageUtil {

    /**
     * 将 PDF 逐页转为压缩后的 PNG 图片
     */
    public static List<BizPptImage> convertPdfToImages(File pdfFile,
                                                       String outputDir,
                                                       String courseDetailId) throws IOException {

        List<BizPptImage> list = new ArrayList<>();

        try (PDDocument doc = PDDocument.load(pdfFile)) {

            PDFRenderer renderer = new PDFRenderer(doc);
            int pageCount = doc.getNumberOfPages();

            for (int i = 0; i < pageCount; i++) {

                // ① 降低 DPI(原 144 → 96)
                BufferedImage sourceImage = renderer.renderImageWithDPI(i, 96);

                // ② 限制最大宽度,等比例缩放
                BufferedImage scaledImage = scaleIfNecessary(sourceImage, 1280);

                String fileName = UUID.randomUUID() + "_" + (i + 1) + ".png";
                File outFile = new File(outputDir, fileName);

                ImageIO.write(scaledImage, "PNG", outFile);

                BizPptImage vo = new BizPptImage();
                vo.setImagePath(outFile.getAbsolutePath());
                vo.setFileName(fileName);
                vo.setCourseDetailId(courseDetailId);

                list.add(vo);
            }
        }

        return list;
    }

    /**
     * 按最大宽度等比例缩放
     */
    private static BufferedImage scaleIfNecessary(BufferedImage source, int maxWidth) {

        int width = source.getWidth();
        int height = source.getHeight();

        if (width <= maxWidth) {
            return source;
        }

        double scale = (double) maxWidth / width;
        int targetWidth = maxWidth;
        int targetHeight = (int) Math.round(height * scale);

        BufferedImage target = new BufferedImage(
                targetWidth,
                targetHeight,
                BufferedImage.TYPE_INT_RGB
        );

        Graphics2D g2d = target.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        g2d.drawImage(source, 0, 0, targetWidth, targetHeight, null);
        g2d.dispose();

        return target;
    }
}

六、参数调优建议

平衡清晰度 + 体积(推荐)

  • DPI = 96
  • maxWidth = 1280

适合 PC 展示场景。

清晰度优先

  • DPI = 120
  • maxWidth = 1600 ~ 1920

适合高分屏场景。

体积优先(缩略图/预览)

  • DPI = 72
  • maxWidth = 800 ~ 1024

图片体积可以大幅降低。

七、优化顺序建议

建议按以下步骤逐步调优:

  • 固定 DPI(例如 96)
  • 调整 maxWidth
  • 观察清晰度和体积
  • 如仍然过大,再降低 DPI

八、如果体积仍然偏大

可以考虑:

改为 JPEG

JPEG 是有损压缩,体积会显著下降。

缺点:

  • 需要自定义 ImageWriter
  • 需要设置压缩质量参数
  • 对文字边缘可能略微模糊

如果对清晰度要求不极端,可以考虑此方案。

九、优化效果验证方式

同一份 PDF:

  • 用老代码生成图片
  • 用新代码生成图片

对比:

ls -lh
du -sh 目录名

重点观察:

  • 单页图片大小
  • 总目录大小
  • 前端加载速度
  • 服务器磁盘增长趋势

十、最终效果

在本项目中:

原方案:

  • DPI = 144
  • 不缩放
  • 单页 2~5MB

优化后:

  • DPI = 96
  • maxWidth = 1280

结果:

  • 单页体积显著下降
  • 总目录大小大幅缩减
  • 前端加载速度明显提升
  • 磁盘增长速度趋缓

并且:

  • 接口签名未变
  • Controller 无感知
  • 业务层零改动

十一、经验总结

PDF 转图片时,一定要考虑:

  • DPI
  • 输出尺寸
  • 图片格式

默认配置往往偏“高清优先”,不适合大规模线上使用。

一句话总结:PDF 转图片不优化,迟早磁盘爆炸。

到此这篇关于Java使用Apache PDFBox进行PDF转图片体积过大的优化指南的文章就介绍到这了,更多相关Java解决PDF转图片体积过大内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解决Idea查看源代码警告Library source does not match the bytecode for class XXX问题

    解决Idea查看源代码警告Library source does not mat

    在使用IDEA开发时,遇到第三方jar包中的源代码和字节码不一致的问题,会导致无法正确打断点进行调试,这通常是因为jar包更新后源代码没有同步更新造成的,解决方法是删除旧的jar包,通过Maven重新下载或手动下载最新的源代码包,确保IDE中的源码与字节码版本一致
    2024-10-10
  • java异步编程详解

    java异步编程详解

    这篇文章主要介绍了java异步编程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • spring基础概念AOP与动态代理理解

    spring基础概念AOP与动态代理理解

    这篇文章主要为大家详细介绍了spring基础概念AOP与动态代理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Java Map集合详解与演示

    Java Map集合详解与演示

    Map用于保存具有映射关系的数据,Map集合里保存着两组值,一组用于保存Map的ley,另一组保存着Map的value,可以理解为Map中的元素是两个对象,一个对象作为键,一个对象作为值。键不可以重复,但是值可以重复
    2021-11-11
  • Java发送带html标签内容的邮件实例代码

    Java发送带html标签内容的邮件实例代码

    下面小编就为大家带来一篇Java发送带html标签内容的邮件实例代码。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • Java并发容器介绍

    Java并发容器介绍

    这篇文章主要介绍了Java并发容器,Java并发包(concurrent)是Java用来处理并发问题的利器,该并发包中主要有原子类,锁(lock),并发容器类等等。本系列博客主要就是介绍并发包中一些常用的并发容器,常用的类,那么我们就来看看下面文章的详细内容吧
    2021-10-10
  • Java中集合遍历的方法示例代码展示

    Java中集合遍历的方法示例代码展示

    在 Java 编程中,集合(Collection)是用于存储和操作一组对象的重要工具,无论是数组、列表(List)、集合(Set),还是映射(Map),它们都提供了在不同场景下灵活使用的数据结构,这篇文章主要介绍了Java中集合遍历的方法示例代码展示,需要的朋友可以参考下
    2024-08-08
  • spring boot2.0图片上传至本地或服务器并配置虚拟路径的方法

    spring boot2.0图片上传至本地或服务器并配置虚拟路径的方法

    最近写了关于图片上传至本地文件夹或服务器,上传路径到数据库,并在上传时预览图片。本文通过实例代码给大家分享spring boot2.0图片上传至本地或服务器并配置虚拟路径的方法,需要的朋友参考下
    2018-12-12
  • jmeter中json提取器如何提取多个参数值

    jmeter中json提取器如何提取多个参数值

    关于jmeter中的正则表达式及json提取器可以提取响应值,但是实际可以需要上个接口的多个响应值,本文就详细的介绍一下如何使用,感兴趣的可以了解一下
    2021-11-11
  • StateMachine 状态机机制深入解析

    StateMachine 状态机机制深入解析

    这篇文章主要介绍了,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08

最新评论