Springboot使用pdfbox提取PDF图片

news2024/11/23 3:56:49

Springboot使用pdfbox提取PDF图片

  • PDFBox的介绍
  • Springboot集成PDFBox
  • 一、提取pdf首页为图像
    • 1. 实现需求
    • 2. 项目代码
    • 3. 执行结果
  • 二、将pdf内容全部转换为图像
    • 1. 实现需求
    • 2. 项目代码
    • 3. 执行结果
    • 4.注意事项
      • 1.优化项目代码
      • 2.提升Java heap size

PDFBox的介绍

PDFBox是一个用于创建和处理PDF文档的Java库。它可以使用Java代码创建、读取、修改和提取PDF文档中的内容。

PDFBox的功能:

  • Extract Text - 使用PDFBox,您可以从PDF文件中提取Unicode文本。

  • Split & Merge - 使用PDFBox,您可以将单个PDF文件分成多个文件,并将它们合并为一个文件。

  • Fill Forms - 使用PDFBox,您可以在文档中填写表单数据。

  • Print - 使用PDFBox,您可以使用标准Java打印API打印PDF文件。

  • Save as Image - 使用PDFBox,您可以将PDF保存为图像文件,如PNG或JPEG。

  • Create PDFs - 使用PDFBox,您可以通过创建Java程序创建新的PDF文件,还可以包含图像和字体。

  • Signing - 使用PDFBox,您可以将数字签名添加到PDF文件。

Springboot集成PDFBox

本项目除了引入pdfbox的依赖之外,还引入了解决图像问题的其他依赖。
例如:jai-imageio-jpeg2000jai-imageio-core是为了解决在转换图像时报错:Cannot read JPEG2000 image: Java Advanced Imaging (JAI) Image I/O Tools are not installed

jbig2-imageio依赖引入是为了解决使用pdfbox2.0将PDF转换为图片时后台报Cannot read JBIG2 image: jbig2-imageio is not installed错误

<!-- pdf提取封面依赖-->
<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>2.0.22</version>
</dependency>
<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox-tools</artifactId>
    <version>2.0.22</version>
</dependency>
<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>jbig2-imageio</artifactId>
    <version>3.0.2</version>
</dependency>
<!-- 解決提取pdf "Cannot read JPEG2000 image"封面失败问题 -->
<dependency>
    <groupId>com.github.jai-imageio</groupId>
    <artifactId>jai-imageio-core</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>com.github.jai-imageio</groupId>
    <artifactId>jai-imageio-jpeg2000</artifactId>
    <version>1.3.0</version>
</dependency>

一、提取pdf首页为图像

1. 实现需求

单个或者批量提取pdf的首页作为封面,或者可以实现提取指定pdf页为图像

2. 项目代码

核心工具类方法:PdfUtils.getPdfFirstImage

package com.zhouquan.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

/**
 * @author ZhouQuan
 * @desciption pdf工具类
 * @date 2023/6/17 9:52
 */
@Slf4j
public class PdfUtils {

    /**
     * 提取pdf首页作为封面
     *
     * @param pdfFile
     * @param dpi     the DPI (dots per inch) to render at
     * @return
     */
    public static BufferedImage getPdfFirstImage(File pdfFile, float dpi) {
        long startTime = System.currentTimeMillis();
        if (!pdfFile.isFile() || !pdfFile.exists()) {
            return null;
        }

        try (PDDocument document = PDDocument.load(pdfFile)) {
            PDFRenderer pdfRenderer = new PDFRenderer(document);

            // 设置页数(首页从0开始)、每英寸点数、图片类型
            BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, dpi, ImageType.RGB);

            log.info("提取耗时:{}ms", System.currentTimeMillis() - startTime);
            return bufferedImage;
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
            return null;
        }
    }
}

service方法类,负责将读取的pdf的bufferedImage对象写入指定的图片对象中

package com.zhouquan.service.impl;

import com.zhouquan.service.PdfService;
import com.zhouquan.utils.PdfUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.pdfbox.tools.imageio.ImageIOUtil;
import org.springframework.stereotype.Service;

import java.awt.image.BufferedImage;
import java.io.File;

/**
 * @author ZhouQuan
 * @desciption pdf提取相关类
 * @date 2023/6/17 9:40
 */
@Slf4j
@Service
public class PdfServiceImpl implements PdfService {

    /**
     * 提取封面的存放路径
     */
    private static String coverPath = "D:/pdf_test/cover";

    /**
     * 提取封面的文件后缀
     */
    private static final String coverExt = "png";

    /**
     * pdf 提取封面
     *
     * @param pdfFile pdf文件
     */
    @Override
    public void pickupCover(File pdfFile) {

        //要渲染的DPI(每英寸点数),可以理解为生成图片的清晰度,值越高生成质量越高
        int dpi = 300;
        try {
            //提取封面工具类
            BufferedImage bufferedImage = PdfUtils.getPdfFirstImage(pdfFile, dpi);

            //获取pdf文件名
            String fileName = FilenameUtils.getBaseName(pdfFile.getName());
            String currentCoverPath = coverPath + "/" + fileName + "." + coverExt;

            // 创建图片文件对象
            FileUtils.createParentDirectories(new File(currentCoverPath));

            // 将图片写入到图片对象中
            ImageIOUtil.writeImage(bufferedImage, currentCoverPath, dpi);

            byte[] coverByte = PdfUtils.bufferedImageToByteArray(bufferedImage);
            log.info("提取封面大小为: {}MB", String.format("%.2f", coverByte.length / 1024 / 1024.0));


        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
}

测试类

package com.zhouquan;

import com.zhouquan.service.PdfService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.io.File;

@SpringBootTest
public class PdfTests {

    @Resource
    public PdfService pdfService;

    /**
     * 提取单个文件封面
     */
    @Test
    public void pickupCover() {
        String pdfFilePath = "D:/pdf_test/pdf/三体三部曲-刘慈欣.pdf";
        pdfService.pickupCover(new File(pdfFilePath), 0);
    }

    /**
     * 批量单个文件封面
     */
    @Test
    public void batchPickupCover() {

        String pdfFilePath = "E:/开发项目/h化工出版社/opt";

        File[] files = new File(pdfFilePath).listFiles();
        if (files != null && files.length > 0) {
            for (File file : files) {
                pdfService.pickupCover(file, 0);
            }
        }
    }
}

3. 执行结果

1.单本pdf提取封面
在这里插入图片描述2.批量提取pdf封面
在这里插入图片描述

二、将pdf内容全部转换为图像

1. 实现需求

将pdf中所有的页转换为图片

2. 项目代码

核心工具类方法:PdfUtils.getPdfAllImage

  /**
     * 加载读取pdf并返回所有的BufferedImage对象
     *
     * @param pdfFile pdf文件对象
     * @param dpi     the DPI (dots per inch) to render at
     * @return
     */
    public static List<BufferedImage> getPdfAllImage(File pdfFile, float dpi) {
        if (!pdfFile.isFile() || !pdfFile.exists()) {
            return null;
        }

        //创建PDFDocument对象并加载PDF文件
        try (PDDocument document = PDDocument.load(pdfFile)) {

            //创建一个PDFRenderer对象并将PDDocument对象传递给它
            PDFRenderer pdfRenderer = new PDFRenderer(document);


            List<BufferedImage> bufferedImages = new ArrayList<>();
            BufferedImage bufferedImage;
            for (int pageIndex = 0; pageIndex < document.getNumberOfPages(); pageIndex++) {
                System.out.println("pageIndex:" + pageIndex);
                // 设置页数(首页从0开始)、每英寸点数、图片类型
                bufferedImage = pdfRenderer.renderImageWithDPI(pageIndex, dpi, ImageType.RGB);
                bufferedImages.add(bufferedImage);
            }

            return bufferedImages;
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

service方法类,负责将读取的pdf的bufferedImage列表对象按顺序写入指定目录的图片文件中

 @Override
    public void pickupPdfToImage(File pdfFile) {

        //要渲染的DPI(每英寸点数),可以理解为生成图片的清晰度,值越高生成质量越高
        int dpi = 100;
        try {
            //提取封面工具类
            List<BufferedImage> pdfAllImage = PdfUtils.getPdfAllImage(pdfFile, dpi);

            log.info("共提取到{}页",pdfAllImage.size());

            String fileName = FilenameUtils.getBaseName(pdfFile.getName());

            String currentCoverPath;
            for (int i = 0; i < pdfAllImage.size(); i++) {
                currentCoverPath = coverPath + "/" + fileName + " 第" + i + "页" + "." + coverExt;

                // 创建图片文件对象
                FileUtils.createParentDirectories(new File(currentCoverPath));

                // 将图片写入到图片对象中
                ImageIOUtil.writeImage(pdfAllImage.get(i), currentCoverPath, dpi);
            }

        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

测试类

/**
  * 批量提取文件封面
  */
 @Test
 public void pickupPdfToImage() {

     String pdfFilePath = "D:/pdf_test/pdf/三体三部曲-刘慈欣.pdf";
     pdfService.pickupPdfToImage(new File(pdfFilePath));

 }

3. 执行结果

在这里插入图片描述

4.注意事项

由于pdf的提取是将pdf文件加载到堆内存中进行操作,因此在提取过程中容易导致堆内存溢出Java heap space,简单来说就是在创建新的对象时, 堆内存中的空间不足以存放新创建的对象,导致此种问题的发生。
解决方案如下:

1.优化项目代码

根据报错信息定位到内存消耗较大的代码,然后对其进行重构或者优化算法。如果是在生产环境,务必要在内存消耗过大的代码出增加日志信息输出,否则容易像我定位一晚上才找到问题所在

2.提升Java heap size

增加堆内存空间设置,此种方式容易操作。可以较快解决当前问题,但是总体来说还是需要找到项目代码中的问题才是最优解,毕竟内存总是有限的

根据自己的硬件配置进行分配对空间,例如8G内存配置的内存参数:

-Xms4096m 
-Xmx4096m

关于pdfbox比较好的学习文档:
https://iowiki.com/pdfbox/pdfbox_overview.html

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

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

相关文章

FDM3D打印系列——1、愉快的给自己打印一个手办

大家好&#xff0c;我是阿赵。 我日常的个人爱好&#xff0c;除了写博客&#xff0c;还有弹吉他打鼓电子琴&#xff0c;还有3D打印。 3D打印只是我的一个业余&#xff0c;不过由于经常把做好的作品发朋友圈&#xff0c;也带动了身边一些朋友买了和我一样型号的打印机&#xff0…

一种说法:哲学是研究真善美的

一种说法&#xff1a;哲学是研究真、善、美的 今天在工作中谈到了真善美 确定一个企业价值观&#xff1a;求真&#xff0c;求善&#xff0c;求美 我觉得挺好&#xff0c;起码无需解释能懂意思 趣讲大白话&#xff1a;真善美是基本问题 【趣讲信息科技199期】 *****************…

用Python做一个下载器,从获取数据到编写GUI界面

本片文章目录 前言案例基本实现思路?代码实现一、单张小说下载二、整本小说下载三、多线程采集四、采集排行榜所有小说五、搜索小说功能六、GUI界面 尾语 前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 对于广大书虫而言&#xff0c;没有小说看是最痛苦的&#xff0…

English Learning - L3 作业打卡 Lesson6 Day42 2023.6.15 周四

English Learning - L3 作业打卡 Lesson6 Day42 2023.6.15 周四 引言&#x1f349;句1: In towns that are near the sea, the tiny lanterns which had been hung in the streets the night before, are placed into the water when the festival is over.成分划分弱读连读爆破…

(二叉树) 129. 求根节点到叶节点数字之和 ——【Leetcode每日一题】

❓129. 求根节点到叶节点数字之和 难度&#xff1a;中等 给你一个二叉树的根节点 root &#xff0c;树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字&#xff1a; 例如&#xff0c;从根节点到叶节点的路径 1 -> 2 -> 3 表示数…

CSS- 横向和纵向时间轴

/*横向时间轴*/.time-horizontal {list-style-type: none;border-top: 1px solid #707070;max-width: 800px;padding: 0px;margin: 0px;}.text-horizontal {list-style-type: none;max-width: 800px;padding: 0px;margin: 0px;}.text-horizontal li {float: left;position: rel…

数据中心网络的电路交换域

buffer 的意义在用带宽平滑统计突发&#xff1a; 流量波动越大&#xff0c;统计复用能效越高。假设没有 buffer&#xff0c;将大量溢出和空载并存。但如果流量是可预期的&#xff0c;也可以转向相反的方向&#xff0c;比如虚电路。 数据中心与 Internet 不同&#xff0c;流量…

90后Android程序员杨国民的羊粪肥创业故事:从社恐到销售奇迹

90后Android程序员杨国民的羊粪肥创业故事&#xff1a;从社恐到销售奇迹 最近一位90后程序员杨国民的创业故事在社交媒体上引起了轰动。他回到了内蒙古老家&#xff0c;并以羊粪肥为主要产品&#xff0c;取得了惊人的销售成绩。据报道&#xff0c;他的羊粪肥月销量达到了120万…

Java选择题刷题记录2

Java集合的关系 图片来自原文链接&#xff1a;https://blog.csdn.net/weixin_45861283/article/details/116201140 HashMap的键可以为null Java基本数据类型&#xff0c;注意String不是基本数据类型 NIO 全称java non-blocking IO &#xff0c;是指 Java 一系列改进的输入…

一篇文章docker-compose安装使用全解

提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 关于docker composedocker compose安装Linux安装docker-composeWindows安装docker-compose docker-compose YMAL常用配置项综合配置示例 docker compose常用命令启动…

设计模式(二十):行为型之迭代器模式

设计模式系列文章 设计模式(一)&#xff1a;创建型之单例模式 设计模式(二、三)&#xff1a;创建型之工厂方法和抽象工厂模式 设计模式(四)&#xff1a;创建型之原型模式 设计模式(五)&#xff1a;创建型之建造者模式 设计模式(六)&#xff1a;结构型之代理模式 设计模式…

LabVIEW开发移动车辆的识别和特征提取

LabVIEW开发移动车辆的识别和特征提取 闭路电视摄像机在高速公路上变得越来越普遍&#xff0c;并用于交通管理;摄像机允许操作员直观地监控交通状况。随着摄像机数量的增加&#xff0c;操作员监控每个摄像机成为一项艰巨的任务&#xff0c;因此录制视频&#xff0c;并且通常仅…

Hack The Box - Web - Phonebook

玩一会儿htb的challenge&#xff0c;最近找工作&#xff0c;所以先玩玩web类型的。 这道题目的类型有人说是LDAP注入、有人说是like注入。LDAP这玩意08年的时候估计可能比较流行&#xff0c;但是现在应该没多少人用了吧&#xff0c;比较小众。其实LDAP这个特殊的数据库是比较契…

LwIP RAW API 实现UDP多播收发

LwIP RAW API 实现UDP多播收发实现 1、初始化 static struct udp_pcb *multicast_pcb NULL; static ip_addr_t mutlcast_send_ip; static ip_addr_t mutlcast_recv_ip;static void udp_recv_multicast(void *arg, struct udp_pcb *pcb, struct pbuf *p,const ip_addr_t *add…

安装cv2库时出现错误的一般解决方法

问题描述&#xff1a; 安装cv2库时出现错误 补充&#xff1a;cv2库的简单介绍 cv2是Python中常用的计算机视觉库OpenCV的Python接口模块。通过使用cv2模块&#xff0c;您可以方便地进行图像和视频的读取、处理和显示等操作。它提供了许多常用的图像处理函数和工具&#xff0…

WPF基础学习笔记3-文本控件

1.文本控件 文本控件System.Windows.Controls,TextBox继承自System.Windows.Controls.TextBoxBase类System.Windows.Controls.RichTextBox继承自System.Windows.Controls.TextBoxBase类 1.1 TextBox 表示一个控件&#xff0c;该控件可用于显示或编辑无格式文本 <Grid>&l…

Python算法练习6.17

leetcode 1768 交替合并字符串 给你两个字符串 word1 和 word2 。请你从 word1 开始&#xff0c;通过交替添加字母来合并字符串。如果一个字符串比另一个字符串长&#xff0c;就将多出来的字母追加到合并后字符串的末尾。 返回 合并后的字符串 。 输入&#xff1a;word1 &qu…

【探索 Kubernetes|作业管理篇 系列 9】Pod 的服务对象

前言 大家好&#xff0c;我是秋意零。 在上一篇中&#xff0c;我们介绍了 Pod 的生命周期以及区分 Pod 字段的层次级别&#xff0c;相信你对此有了充分的认识。 今天&#xff0c;我们还会接着以 Pod 展开&#xff0c;说说它的 “服务对象”&#xff0c;一听就知道是对 Pod 提…

RocketMQ_高级功能

目录 一、消息存储 1、存储介质以及性能对比 2、消息的存储和发送 3、消息存储结构 4、刷盘机制 二、高可用性机制 1、消息消费高可用 2、消息发送高可用 3、消息主从复制 三、负载均衡 1、Producer负载均衡 2、Consumer负载均衡 四、消息重试 1、顺序消息的重试…

微信无人托管智能客服系统

随着人工智能技术的不断发展&#xff0c;大语言模型、智能客服、垂直化场景应用和微信聊天等三方终端系统已经成为了企业营销的重要工具。这些技术的结合可以帮助企业更好地与客户进行沟通&#xff0c;提高客户满意度和忠诚度&#xff0c;从而实现营销目标。 大语言模型可以帮…