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.rhkb.cn/news/460917.html

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

相关文章

【HarmonyOS NEXT】使用 Navigation 对折叠屏设备页面进行分栏展示,优化 UI 交互

关键词&#xff1a;折叠屏、navigation、router、路由、分栏、UI 随着科技的发展&#xff0c;手机设备形态也由一面屏向多面屏进行发展&#xff0c;那么软件的UI适配也面临着问题&#xff0c;本篇文章主要解决大屏设备的页面 UI 适配问题&#xff0c;如折叠屏&#xff0c;平板&…

Coppelia Sim (v-REP)仿真 机器人3D相机手眼标定与实时视觉追踪 (二)

coppelia sim[V-REP]仿真实现 机器人于3D相机手眼标定与实时视觉追踪 二 zmq API接口python调用python获取3D相机的数据获取彩色相机的数据获取深度相机的数据用matpolit显示 python控制机器人运动直接控制轴的位置用IK运动学直接移动到末端姿态 相机内参的标定记录拍照点的位置…

Linux 安装nacos

1.下载版本 https://github.com/alibaba/nacos/tags 2.解压压缩包&#xff0c;启动 (1)将压缩包放到/usr/local目录下&#xff0c;解压 tar -xvf nacos-server-2.0.0-BETA.tar.gz(2)删除压缩包 rm -f nacos-server-2.0.0-BETA.tar.gz(3) 找到nacos的mysql的数据库脚本,在数…

sqoop问题汇总记录

此篇博客仅记录在使用sqoop时遇到的各种问题。持续更新&#xff0c;有问题评论区一起探讨&#xff0c;写得有不足之处见谅。 Oracle_to_hive 1. main ERROR Could not register mbeans java.security.AccessControlException: access denied ("javax.management.MBeanTr…

基于微信小程序的小区管理系统设计与实现(lw+演示+源码+运行)

摘 要 社会发展日新月异&#xff0c;用计算机应用实现数据管理功能已经算是很完善的了&#xff0c;但是随着移动互联网的到来&#xff0c;处理信息不再受制于地理位置的限制&#xff0c;处理信息及时高效&#xff0c;备受人们的喜爱。所以各大互联网厂商都瞄准移动互联网这个潮…

window11使用wsl2安装Ubuntu22.04

目录 1、快速了解wsl2 安装子系统linux流程&#xff08;B站视频&#xff09; 2、wsl2常用命令 3、windows与子系统Linux文件访问方法 4、子系统linux使用windows网络代理、网络配置&#xff08;镜像网络&#xff0c;非NAT&#xff09; 5、wsl2 Ubuntu miniconda 安装 6、…

【K8S系列】Kubernetes 中 NodePort 类型的 Service 无法访问的问题【已解决】

在 Kubernetes 中&#xff0c;NodePort 类型的 Service 允许用户通过每个节点的 IP 地址和指定的端口访问应用程序。如果 NodePort 类型的 Service 无法通过节点的 IP 地址和指定端口进行访问&#xff0c;可能会导致用户无法访问应用。本文将详细分析该问题的常见原因及其解决方…

Cyber​​Panel filemanager/upload 远程命令执行漏洞复现

0x01 产品简介 CyberPanel是一个开源的Web控制面板,它提供了一个用户友好的界面,用于管理网站、电子邮件、数据库、FTP账户等。CyberPanel旨在简化网站管理任务,使非技术用户也能轻松管理自己的在线资源。 0x02 漏洞概述 该漏洞源于filemanager/upload接口未做身份验证和…

(C#面向初学者的 .NET 的生成 AI) 第 2 部分-什么是 AI 和 ML?

从本部分开始Luis Quintanilla介绍AI和机器学习&#xff0c;需要学习的一些东西是什么是AI和ML&#xff1f;作为一名.net开发人员如何学习使用AI和ML。 1、首先什么是AI 和 ML&#xff1f; 你可以把它看作是基本相同事物的不同层次。 在顶层的是AI&#xff08;人工智能&#xf…

Swarm-LIO: Decentralized Swarm LiDAR-inertial Odometry论文翻译

文章目录 前言一、介绍二、相关工作三、方法A. 问题表述B. 框架概述C. 群体系统的初始化D. 去中心化激光雷达-惯性状态估计 四. 实验A. 室内飞行B. 退化环境飞行C. 去中心化部署 五. 结论和未来工作 前言 原文&#xff1a;原文 准确的自我状态和相对状态估计是完成群体任务的关…

wsl 使用docker 部署oracle11g数据库

wsl 使用docker 部署oracle11g数据库 1. 下载oracle11g sudo docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g2. 运行oracle11g容器&#xff08;docker-compose&#xff09; services:oracle-1.0:container_name: oracle11gimage: oracle11g:1.0restart:…

IDEA集成JProfiler

目录 下载并安装JProfiler下载安装包管理员身份运行配置许可证邮箱复制注册码配置永久许可证选择IDE集成 在IDEA中下载并安装JProfiler插件启动并使用JProfiler进行性能分析启动Java应用程序&#xff1a;自动运行JProfiler 知识扩充功能 下载并安装JProfiler 下载安装包 官网…

Tomcat 和 Docker部署Java项目的区别

在 Java 项目部署中&#xff0c;Tomcat 和 Docker 是两种常见的选择。虽然它们都可以用来运行 Java 应用&#xff0c;但它们在定位、部署方式、依赖环境、资源隔离、扩展性和适用场景等方面有显著区别。 1. 功能定位 1.1 Tomcat Apache Tomcat 是一种轻量级的 Java 应用服务器…

AI-基本概念-多层感知器模型/CNN/RNN/自注意力模型

1 需求 神经网络 …… 深度学习 …… 深度学习包含哪些神经网络&#xff1a; 全连接神经网络卷积神经网络循环神经网络基于注意力机制的神经网络 2 接口 3 CNN 在这个示例中&#xff1a; 首先定义了一个简单的卷积神经网络SimpleCNN&#xff0c;它包含两个卷积层、两个池…

Leaflet查询矢量瓦片偏移的问题

1、问题现象 使用Leaflet绘制工具查询出来的结果有偏移 2、问题排查 1&#xff09;Leaflet中latLngToContainerPoint和latLngToLayerPoint的区别 2&#xff09;使用Leaflet查询需要使用像素坐标 3&#xff09;经排查发现&#xff0c;container获取的坐标是地图容器坐标&…

Vue生成名片二维码带logo并支持下载

一、需求 生成一张名片&#xff0c;名片上有用户信息以及二维码&#xff0c;名片支持下载功能&#xff08;背景样式可更换&#xff0c;忽略本文章样图样式&#xff09;。 二、参考文章 这不是我自己找官网自己摸索出来的&#xff0c;是借鉴各位前辈的&#xff0c;学以致用&am…

如何利用网站进行仿牌运营?

对于很多人来说&#xff0c;仿牌网站的运营是一项充满挑战的任务&#xff0c;很多初学者对如何开始感到困惑&#xff0c;甚至不清楚仿牌网站的运作机制。此外&#xff0c;搜索引擎对新网站的审核期也使得许多站长倍感压力。那么&#xff0c;如何才能在这一过程中有效地进行SEO优…

数字IC开发:布局布线

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

传智杯 第六届-复赛-C

题目描述&#xff1a; 小红有一个数组&#xff0c;她每次可以选择数组的一个元素 xxx &#xff0c;将这个元素分成两个元素 aaa 和 bbb &#xff0c;使得 abxabxabx。请问小红最少需要操作多少次才可以使得数组的所有元素都相等。 输入描述: 第一行输入一个整数 n(1≤n≤10^5)…

华为配置 之 GVRP协议

目录 简介&#xff1a; 配置GVRP&#xff1a; 总结&#xff1a; 简介&#xff1a; GVRP&#xff08;GARP VLAN Registration Protocol&#xff09;&#xff0c;称为VLAN注册协议&#xff0c;是用来维护交换机中的VLAN动态注册信息&#xff0c;并传播该信息到其他交换机中&…