Java如何实现PDF转高质量图片

在这里插入图片描述
大家好,我是 V 哥。在Java中,将PDF文件转换为高质量的图片可以使用不同的库,其中最常用的库之一是 Apache PDFBox。通过该库,你可以读取PDF文件,并将每一页转换为图像文件。为了提高图像的质量,你可以指定分辨率等参数。此外,也可以结合 Java ImageIO 来保存生成的图片文件。

如何实现

下面V哥通过一个详细的案例,来展示如何使用 PDFBox 实现 PDF 转高质量图片:

所需依赖

首先,确保你已经在项目中添加了 PDFBox 依赖。你可以通过Maven来添加:

<dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.29</version> <!-- 确保使用最新的版本 -->
</dependency>

实现步骤

先来捋一下实现步骤哈。

  1. 加载 PDF 文件
  2. 设置渲染参数(如 DPI 来控制图片分辨率)
  3. 将每页 PDF 渲染为图片
  4. 保存图片

通过以上1,2,3,4个步骤,咱们具体来实现一下代码:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;public class VGPdfToImage {public static void main(String[] args) {// PDF文件路径String pdfFilePath = "path/to/your/pdf/vg_doc.pdf";// 输出图片文件夹路径String outputDir = "path/to/output/images/";// 设置DPI(越高图片越清晰,但文件也会更大)int dpi = 300;try (PDDocument document = PDDocument.load(new File(pdfFilePath))) {PDFRenderer pdfRenderer = new PDFRenderer(document);// 遍历PDF每一页并转换为图片for (int page = 0; page < document.getNumberOfPages(); ++page) {// 使用BufferedImage来表示图像BufferedImage bim = pdfRenderer.renderImageWithDPI(page, dpi);// 生成文件名String fileName = outputDir + "pdf_page_" + (page + 1) + ".png";// 将图片保存为PNG格式ImageIO.write(bim, "png", new File(fileName));System.out.println("Saved page " + (page + 1) + " as image.");}} catch (IOException e) {e.printStackTrace();}}
}

来解释一下

  1. PDFRenderer: PDFBox 提供的 PDFRenderer 类用于将 PDF 文档页渲染为图像对象(BufferedImage)。
  2. renderImageWithDPI: 该方法可以指定DPI(每英寸点数),它直接影响图片的分辨率。通常,72 DPI 是屏幕显示的默认分辨率,而300 DPI 被视为高质量打印的分辨率。
  3. ImageIO: Java的 ImageIO 用于将 BufferedImage 保存为 PNG、JPEG 等常见图片格式。

输出效果

  • 每一页的PDF将被单独渲染为一张图片,并且通过高DPI参数设置,图片的质量较高。
  • 输出的文件路径为 outputDir 指定的路径,图片将被保存为PNG格式。你也可以更改保存格式为JPEG等。

可调整的项有

  • DPI 设置: 如果你希望输出更高质量的图片,可以将 DPI 设置为 300 或更高。如果需要快速渲染且质量要求不高,可以设置为72 DPI。
  • 图片格式: ImageIO.write() 可以使用不同的格式,如 "jpg""png",根据需求调整。

注意一下,确保你的PDFBox库版本是较新的版本,如2.x系列,来保证支持更多的PDF功能和修复潜在问题。

以上就是一个简单的实现过程DEMO,那在实际应用中,一定会有特定问题,问题来了,如何你要处理的 PDF 文件比较大,或者页数比较多,那必定是要考虑性能问题滴。就这两个问题,V 哥来优化一下。

两个可能的性能优化问题

  1. 缓存策略:对于较大的 PDF 文件,你可以使用某些缓存策略来优化性能。
  2. 并行处理:如果你需要处理很多页的 PDF,可以通过多线程并行处理每一页以提升速度。

缓存策略优化

当要处理较大的 PDF 文件时,咱们使用缓存策略可以显著优化性能,特别是对于那些需要处理多个页面或反复渲染的情况。对于 PDF 渲染操作,缓存策略主要是为了减少对磁盘或内存的反复访问,从而加快读取、渲染速度并节省内存。

在 Java 中,可以通过以下几种方式实现缓存优化:

  1. 内存缓存:将已处理的页面保存在内存中,当需要重复访问这些页面时直接从缓存中获取。
  2. 磁盘缓存:如果内存不足以缓存所有页面,可以将页面渲染结果或部分中间数据缓存到磁盘上。
  3. 逐页处理:只在需要时加载并处理某些页面,而不是一次性加载整个PDF文件。

采用实现内存缓存的案例

采用内存缓存,咱们可以使用 ConcurrentHashMap 来实现,将已经渲染的 PDF 页面存储在内存中,避免重复渲染。

来看一个使用内存缓存的详细实现案例:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;public class PdfToImageWithCache {// 用于缓存已渲染的PDF页面(使用ConcurrentHashMap确保线程安全)private static final ConcurrentHashMap<Integer, BufferedImage> imageCache = new ConcurrentHashMap<>();private static final int dpi = 300; // 高质量DPI设置public static void main(String[] args) {// PDF文件路径String pdfFilePath = "path/to/your/large/pdf/ vg_doc.pdf";// 输出图片文件夹路径String outputDir = "path/to/output/images/";try (PDDocument document = PDDocument.load(new File(pdfFilePath))) {PDFRenderer pdfRenderer = new PDFRenderer(document);// 获取页面总数int totalPages = document.getNumberOfPages();System.out.println("Total pages: " + totalPages);// 渲染并缓存每一页for (int page = 0; page < totalPages; ++page) {BufferedImage image = renderPageWithCache(pdfRenderer, page);// 保存图片String fileName = outputDir + "pdf_page_" + (page + 1) + ".png";ImageIO.write(image, "png", new File(fileName));System.out.println("Saved page " + (page + 1) + " as image.");}} catch (IOException e) {e.printStackTrace();}}/*** 使用缓存渲染PDF页面* @param pdfRenderer PDFRenderer实例* @param page 页码(从0开始)* @return 缓存或渲染后的BufferedImage*/private static BufferedImage renderPageWithCache(PDFRenderer pdfRenderer, int page) throws IOException {// 检查缓存是否已存在该页面的图像if (imageCache.containsKey(page)) {System.out.println("Page " + (page + 1) + " found in cache.");return imageCache.get(page);}// 如果缓存中不存在,则渲染并存入缓存System.out.println("Rendering page " + (page + 1) + "...");BufferedImage image = pdfRenderer.renderImageWithDPI(page, dpi);imageCache.put(page, image);return image;}
}
解释一下代码
  1. 内存缓存(ConcurrentHashMap:

    • 使用 ConcurrentHashMap<Integer, BufferedImage> 作为缓存结构,Integer 代表页面的索引(从0开始),BufferedImage 代表已渲染的图像。
    • 每次渲染页面前,先检查缓存中是否存在该页面的图像,如果已存在,则直接返回缓存的图像,否则渲染并保存到缓存中。
  2. renderPageWithCache 方法:

    • 该方法首先检查页面是否在缓存中,如果在,则直接从缓存中获取。
    • 如果缓存中不存在该页面的图像,则渲染并将其保存到缓存中。
  3. DPI 设置:

    • dpi 参数设置为300以确保输出的图像质量足够高。
  4. 逐页渲染:

    • 使用 for 循环逐页处理,避免一次性加载所有页面到内存。对于每页图像的渲染,若该页面已经渲染过,则直接从缓存中获取。
这样优化的好处是啥
  1. 内存缓存的好处:

    • 当你需要多次访问或保存某些页面时,内存缓存可以避免重复渲染,从而提升性能。
    • 对于较大的PDF文件,如果反复操作相同的页面,缓存能显著减少处理时间。
  2. 并发支持:

    • ConcurrentHashMap 保证了在多线程环境下缓存操作的安全性,可以安全地在多线程中使用。
  3. 控制内存占用:

    • 如果内存使用量过大,可以根据情况定期清理缓存,或者在缓存中限制最大保存数量,使用类似LRU(最近最少使用)策略来清除旧缓存。

实现磁盘缓存的案例

接下来,咱们看一个使用磁盘缓存要怎么实现,如果 PDF 文件较大,内存无法保存全部页面的图像,我的天啊,那要怎么办?就是可以使用磁盘缓存,将渲染结果暂时保存到磁盘。

来看下面这个磁盘缓存策略实现,将渲染的图像保存为临时文件,并在需要时从磁盘加载:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;public class PdfToImageWithDiskCache {private static final int dpi = 300; // 高质量DPI设置private static final String cacheDir = "path/to/cache/";public static void main(String[] args) {// PDF文件路径String pdfFilePath = "path/to/your/large/pdf/vg_doc.pdf";// 输出图片文件夹路径String outputDir = "path/to/output/images/";try (PDDocument document = PDDocument.load(new File(pdfFilePath))) {PDFRenderer pdfRenderer = new PDFRenderer(document);int totalPages = document.getNumberOfPages();for (int page = 0; page < totalPages; ++page) {BufferedImage image = renderPageWithDiskCache(pdfRenderer, page);// 保存图片String fileName = outputDir + "pdf_page_" + (page + 1) + ".png";ImageIO.write(image, "png", new File(fileName));System.out.println("Saved page " + (page + 1) + " as image.");}} catch (IOException e) {e.printStackTrace();}}/*** 使用磁盘缓存渲染PDF页面* @param pdfRenderer PDFRenderer实例* @param page 页码(从0开始)* @return 缓存或渲染后的BufferedImage*/private static BufferedImage renderPageWithDiskCache(PDFRenderer pdfRenderer, int page) throws IOException {// 磁盘缓存文件路径File cachedFile = new File(cacheDir + "page_" + page + ".png");// 如果缓存文件已存在,则从磁盘加载if (cachedFile.exists()) {System.out.println("Loading page " + (page + 1) + " from disk cache.");return ImageIO.read(cachedFile);}// 如果缓存文件不存在,则渲染并保存到磁盘System.out.println("Rendering page " + (page + 1) + "...");BufferedImage image = pdfRenderer.renderImageWithDPI(page, dpi);ImageIO.write(image, "png", cachedFile);return image;}
}

代码解释

  1. 缓存到磁盘: 通过 ImageIO.write() 将渲染的图像保存到磁盘上,如果该页面已经有缓存文件,则直接从磁盘读取。
  2. 缓存文件路径: 每个页面有对应的缓存文件名,避免重复渲染和保存。
  3. 适用于内存不足的情况: 当内存不足时,可以通过磁盘缓存减轻内存负担,同时仍然保留较好的访问速度。

通过这样的优化策略,咱们就可以在处理较大的 PDF 文件时,显著提升性能并减少资源消耗。

并行处理优化

接下来,看第二个问题:在处理很多页的 PDF 文件时,通过多线程并行处理每一页可以让处理速度显著提升,尤其是在每页渲染操作耗时较长的情况下。Java 提供了多线程的机制,咱们就用 ExecutorService 可以方便地管理和执行多线程任务。

下面来看一下如何实现哈,使用多线程并行处理 PDF 文件的每一页,将其转换为高质量图片。

主要步骤有三个

  1. 使用 ExecutorService 来创建线程池
  2. 每个线程独立处理一页 PDF,将其渲染为图片。
  3. 线程任务执行完毕后,统一关闭线程池

具体的代码实现

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class PdfToImageWithMultithreading {// 设置DPI用于高质量渲染private static final int dpi = 300;public static void main(String[] args) {// PDF文件路径String pdfFilePath = "path/to/your/large/pdf/vg_doc.pdf";// 输出图片文件夹路径String outputDir = "path/to/output/images/";// 线程池大小(可以根据CPU核心数量或需要并行的任务数进行调整)int numThreads = Runtime.getRuntime().availableProcessors();ExecutorService executorService = Executors.newFixedThreadPool(numThreads);try (PDDocument document = PDDocument.load(new File(pdfFilePath))) {PDFRenderer pdfRenderer = new PDFRenderer(document);int totalPages = document.getNumberOfPages();System.out.println("Total pages: " + totalPages);// 为每一页创建一个并行处理任务for (int page = 0; page < totalPages; page++) {final int currentPage = page;  // 需要用final修饰以便在多线程中使用executorService.submit(() -> {try {renderAndSavePage(pdfRenderer, currentPage, outputDir);} catch (IOException e) {e.printStackTrace();}});}} catch (IOException e) {e.printStackTrace();} finally {// 关闭线程池executorService.shutdown();try {// 等待所有线程任务完成if (!executorService.awaitTermination(60, TimeUnit.MINUTES)) {System.err.println("Some tasks did not finish within the timeout.");}} catch (InterruptedException e) {e.printStackTrace();}}}/*** 渲染PDF页面并保存为图片* @param pdfRenderer PDFRenderer实例* @param page 页码(从0开始)* @param outputDir 输出目录* @throws IOException 如果发生IO错误*/private static void renderAndSavePage(PDFRenderer pdfRenderer, int page, String outputDir) throws IOException {// 渲染页面为高质量图片BufferedImage image = pdfRenderer.renderImageWithDPI(page, dpi);// 保存图片文件String fileName = outputDir + "pdf_page_" + (page + 1) + ".png";ImageIO.write(image, "png", new File(fileName));System.out.println("Saved page " + (page + 1) + " as image.");}
}

来详细解释一下代码和思路

1. 线程池的使用
  • ExecutorService :我们使用 Executors.newFixedThreadPool(numThreads) 来创建一个固定大小的线程池,其中 numThreads 是线程的数量。通过 Runtime.getRuntime().availableProcessors() 获取 CPU 核心数作为线程池大小的依据,通常这个值是处理器核心数。
  • submit() :将任务提交给线程池,submit() 方法会立即返回,不会阻塞主线程,从而能够让多个页面同时处理。
2. 任务分配
  • 每一页的渲染任务被分配到一个线程中,通过 executorService.submit() 提交渲染任务。每个任务都会调用 renderAndSavePage() 方法,处理特定页面的渲染和保存。
3. 渲染与保存
  • 每个线程使用 renderAndSavePage() 方法渲染指定页码的 PDF,并将生成的图像保存为 PNG 文件。这里使用 ImageIO.write() 来保存渲染结果。
  • 输出的文件名根据页面编号动态生成。
4. 关闭线程池
  • shutdown() :主线程在提交所有任务后调用 shutdown() 方法,通知线程池停止接收新的任务。
  • awaitTermination():主线程等待所有线程任务完成,这里设置了一个较长的超时时间(60分钟),你要根据实际情况来调整一下,确保所有页都能被处理完毕。

小结一下

通过多线程处理PDF的每一页,能显著缩短处理时间,特别是在处理大文件或大量页数的PDF时。线程池中的任务可以同时在多个CPU核心上运行,最大化利用硬件资源。对于超级大PDF文件或需要处理大量PDF时,可那就得上分布式处理了,每个节点处理一部分页面来解决,这里就不多赘述了。

最后

Java 如何实现PDF转高质量图片的案例就讲完了,喜欢这篇文件的话,一定帮我点赞、评论支持哦,如果怕忘了,收藏起来备孕是不错的选择。关注威哥爱编程,一群人的坚持才更加快乐。么么哒~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/58704.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文略读:OneChart: Purify the Chart Structural Extraction via One Auxiliary Token

2024 旷视的work 图表解析模型 1 背景 对于之前的视觉语言模型&#xff0c;论文认为其有两点不足需要改进&#xff1a; 需要充分训练一个真正会看 chart 的 vision encoder单纯对文本输出算交叉熵损失&#xff0c;并不是最优的&#xff08;如上图所示&#xff0c;当ground-tr…

STM32CubeMX学习(三) SPI+DMA通信

STM32CubeMX学习&#xff08;三&#xff09; SPIDMA通信 一、简介二、新建STM32CubeMX项目并使用外部时钟三、SPI3配置四、相关代码五、测试 一、简介 本文将基于STM32F103RCT芯片介绍如何在STM32CubeMXKEIL5开发环境下进行SPIDMA通信。 操作系统&#xff1a;WIN10 x64硬件电…

iOS静态库(.a)及资源文件的生成与使用详解(OC版本)

引言 iOS静态库&#xff08;.a&#xff09;及资源文件的生成与使用详解&#xff08;Swift版本&#xff09;_xcode 合并 .a文件-CSDN博客 在前面的博客中我们已经介绍了关于iOS静态库的生成步骤以及关于资源文件的处理&#xff0c;在本篇博客中我们将会以Objective-C为基础语言…

Python爬虫:在1688上“拍立淘”——按图索骥的奇妙之旅

想象一下&#xff0c;你是一名古代的侦探&#xff0c;手中握着一张神秘的藏宝图&#xff0c;在1688的茫茫商品海洋中寻找与之匹配的宝藏。今天&#xff0c;我们将一起化身为代码界的“拍立淘”专家&#xff0c;使用Python爬虫技术&#xff0c;通过API接口按图搜索商品。准备好你…

如何在小红书发布笔记时显示外地IP地址

小红书平台在发布笔记时显示IP地址可能是由于网络爬虫或者某些技术手段抓取数据时所导致的。为了保护用户隐私和安全&#xff0c;显示外地IP地址&#xff0c;可以尝试以下几种方法&#xff1a; 1.检查发布环境&#xff1a; 确保你是在一个安全、可信的网络环境下发布笔记&…

Linux中查询Redis中的key和value(没有可视化工具)

1.进入redis安装目录 进入redis安装目录,找到redis-cli(redis的客户端) 2.登录redis客户端 登录redis的客户端,格式:redis-cli -h [host] -p [port] -a [password],懂的都懂!!! ./redis-cli -h 192.168.8.101 -p 6380 -a xxxx登录成功后就这样子 3.查看redis中所有的key和…

Unity Editor 快速移动资源

Editor 快速移动资源 &#x1f354;使用场景&#x1f32d;功能 &#x1f354;使用场景 一般想要移动一个资源到另一个目录的办法是选中资源拖拽过去&#xff0c; 但在一个比较大的项目中你得一直拖啊拖直到找到那个目录 &#x1f92f;。 使用本插件就可以省去拖拽的步骤&#…

特斯联巨亏数十亿:毛利率剧烈波动下滑,高管动荡引发关注

《港湾商业观察》施子夫 近期&#xff0c;重庆特斯联智慧科技股份有限公司&#xff08;以下简称&#xff0c;特斯联&#xff09;递表港交所&#xff0c;联席保荐机构中信证券和海通国际。 此番闯关港交所&#xff0c;特斯联三年半巨亏超70亿元&#xff0c;公司何时能扭亏为盈…

javaweb----VS code

前端开发神器&#xff1a;VS Code → 速度快、体积小、插件多 VS Code 安装官网&#xff1a;https://code.visualstudio.com/download VS Code一些必备的插件安装&#xff1a; 1、Chinese (Simplified) 简体中文 2、Code Spell Checker 检查拼写 3、HTML CSS Support 4…

使用 Kafka 和 MinIO 实现人工智能数据工作流

MinIO Enterprise Object Store 是用于创建和执行复杂数据工作流的基础组件。此事件驱动功能的核心是使用 Kafka 的 MinIO 存储桶通知。MinIO Enterprise Object Store 为所有 HTTP 请求&#xff08;如 PUT、POST、COPY、DELETE、GET、HEAD 和 CompleteMultipartUpload&#xf…

【Python】数据容器详解:列表、元组、字典与集合的推导式与公共方法

目录 &#x1f354; 列表集合字典的推导式 1.1 什么是推导式 1.2 为什么需要推导式 1.3 列表推导式 1.4 列表推导式 if条件判断 1.5 for循环嵌套列表推导式 1.6 字典推导式 1.7 集合推导式 &#x1f354; 数据序列中的公共方法 2.1 什么是公共方法 2.2 常见公共方法…

基于Unet卷积神经网络的脑肿瘤MRI分割

项目源码获取方式见文章末尾&#xff01; 回复暗号&#xff1a;13&#xff0c;免费获取600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【YOLO模型实现农作物病虫害虫识别带GUI界面】 2.【卫星图像道路检测DeepLabV3P…

Unity XR Interaction Toolkit 开发教程(1):OpenXR 与 XRI 概述【3.0 以上版本】

文章目录 &#x1f4d5;Unity XR 开发架构&#x1f50d;底层插件&#xff08;对接硬件&#xff09;&#x1f50d;高层 SDK&#xff08;面向应用交互层&#xff09; &#x1f4d5;OpenXR&#x1f4d5;XR Interaction Toolkit&#x1f50d;特点&#x1f50d;XRI 能够实现的交互类…

Diving into the STM32 HAL-----Interrupts

硬件管理就是处理异步事件。其中大部分来自硬件外围设备。例如&#xff0c;计时器达到配置的 period 值&#xff0c;或者 UART 在数据到达时发出警告。 中断是一个异步事件&#xff0c;它会导致按优先级停止执行当前代码&#xff08;中断越重要&#xff0c;其优先级越高;这将导…

Linux中SPI

参考资料 https://www.cnblogs.com/aaronLinux/p/6219146.html1.SPI 2.SPI传输 2.1传输示例 首先&#xff0c;CS0拉低选中的SPI Flash , 然后在每个时钟周期&#xff0c; DO输出对应的电平。 SPI FLASH会在每个时钟的上升沿读取D0的电平。2.2SPI模式 根据SCK的电平以及数据在…

自旋锁--死锁

本文内容整理自B站视频教程 自旋锁定义 内核发生访问资源冲突的时候&#xff0c;可以有两种锁的解决方案选择&#xff1a;一个是原地等待&#xff0c;一个是挂起当前进程&#xff0c;调度其他进程执行(休眠)。 spinlock是内核中提供的一种比较常见的锁机制&#xff0c;自旋锁…

【C++】继承和多态常见的面试问题

文章目录 继承笔试面试题1. 什么是菱形继承&#xff1f;菱形继承的问题是什么&#xff1f;2. 什么是菱形虚拟继承&#xff1f;如何解决数据冗余和二义性&#xff1f;3. 继承和组合的区别&#xff1f;什么时候用继承&#xff1f;什么时候用组合&#xff1f; 选择题 多态概念考察…

数字IC开发:布局布线

数字IC开发&#xff1a;布局布线 前端经过DFT&#xff0c;综合后输出网表文件给后端&#xff0c;由后端通过布局布线&#xff0c;将网表转换为GDSII文件&#xff1b;网表文件只包含单元器件及其连接等信息&#xff0c;GDS文件则包含其物理位置&#xff0c;具体的走线&#xff1…

Python爬虫的“京东大冒险”:揭秘商品类目信息

开篇&#xff1a;欢迎来到Python的奇幻森林 在这个数据驱动的时代&#xff0c;我们就像一群探险家&#xff0c;穿梭在数字的森林中&#xff0c;寻找着隐藏的宝藏——商品类目信息。今天&#xff0c;我们将带领你一起&#xff0c;用Python这把锋利的剑&#xff0c;深入京东的神…

Flutter 13 网络层框架架构设计,支持dio等框架。

在移动APP开发过程中&#xff0c;进行数据交互时&#xff0c;大多数情况下必须通过网络请求来实现。客户端与服务端常用的数据交互是通过HTTP请求完成。面对繁琐业务网络层&#xff0c;我们该如何通过网络层架构设计来有效解决这些问题&#xff0c;这便是网络层框架架构设计的初…