学习笔记(Java篇):Java操作文件之PDF(1)

时间:2025-03-21 15:15:44

1、前言

        开发过程中遇到的问题:将一个PDF文件进行拆分,例如该文件有20页,需要将其拆分成20个文件单独存放每一页(感觉很离谱的需求),亦或是将指定某些页单独取出来。又或者说是需要将PDF文件进行转储图片文件,因为前端读取图片要比读取文件展示的效率高一些。所以本篇将介绍Java操作PDF将其进行拆分或者转储图片。

2、简述思路

        1、首先将传入的文件转换成流,转换成流之后,可以使用apache的pdfbox中的PDDocument对象加载这个流从而使其变成一个可作为PDF操作的文件对象。

        2、PDDocument对象中提供了很多个可供使用的API,我们可以直接调用其中的API进行操作PDF文件。

        3、然后PDDocument还可以被转换为另一个对象PDFRenderer,此类可以将PDF文件渲染成图片的格式。

3、PDF拆分 

        在操作PDF之前需要得到对应的PDF对象,无论我们得到的是文件连接还是File对象,都必须先将其转换为流的形式。

//通过文件连接转化成流
InputStream inputStream = (pdfUrl);
//通过File对象转化成为流
File file = new File(filePath);//文件路径
InputStream inputStream = new FileInputStream(file);
//将文件流转化成可操作的PDDocument对象
PDDocument document = (inputStream);

然后就可以直接操作这个PDDocument对象对其进行拆分。在PDDocument对象提供的API中,可以直接获取到PDF文件的页码信息。

下面简单列举一些此处用到的方法,其余未用到的暂时不列举,可参考源码:

1、(int pageIndex);获取第N页,返回一个PDPage对象。

2、();获取所有页,返回一个PDPageTree包含所有页数据的对象。

3、();底层还是().getCount();获取该文件总共有多少页。

4、(PDPage page);为当前PDF添加一页(尾部追加)。

5、(OutputStream os);将该PDF作为输出流进行保存。初次之外还支持传入File对象与文件名的字符串类型,这些是用作保存为文件类型。

6、();在操作完毕之后执行关闭Document对象(下面再介绍为何必须执行)。

上代码:

public static void main(String[] args) {
        String inputFilePath = "path/to/";
        String outputDirectory = "path/to/output/directory";

        try {
            //加载文件使之成为流
            InputStream inputStream = (pdfUrl);
            //转化为PDDocument对象
            PDDocument document = (inputStream);
            //获取总页数
            int pageCount = ();
            //循环分别将每一页输出到指定路径
            for (int i = 0; i < pageCount; i++) {
                
                PDPage page = (i);

                PDDocument newDocument = new PDDocument();
                (page);
                //拼出文件名(包含本地路径)
                String outputFilePath = outputDirectory +  + "page_" + (i + 1) + ".pdf";
                (outputFilePath);
                ();
            }
            ();
        } catch (IOException e) {
            ();
        }
    }

这样就完成了文件拆分输出,如若想要按照指定条件拆分,例如1-10页作为一个文件输出,11-20合并输出等,只需要改变循环条件,并按照需要对newDocument进行addPage();即可。

4、转储图片

        PDF文件可能在前端加载起来需要较长的时间,但是图片的效率会更高,所以有时候会需要将PDF文件转化为图片格式进行输出。

        这就牵涉到另一个对象:PDFRenderer.这个类支持将PDF渲染成图片的形式。简单介绍一下用到的方法:

1、(int pageIndex, float scale);此方法将执行页码的PDF渲染成图片,scale指的是渲染图片的缩放比例,可以不传,不传默认为1是原始大小,1=72DPI

2、(int pageIndex, float dpi, ImageType type);功能同上,可指定DPI。ImageType用于指定图片渲染方式,不传默认为RGB(意为red,green,blue红绿蓝三原色渲染)。

PDFRenderer renderer = new PDFRenderer(document);
BufferedImage image = (page, scale);
//将图片写到指定路径
(image, "jpg", new File(path));

像这样将其渲染成指定图片之后,就可以对其进行保存等操作。

5、图片拼接

        关于BufferedImage,在此做一下简单的介绍:这是一个Image的实现类,可以将图片加载到内存,通过此类,可以对图像进行一些简单的操作。

        创建对象的时候,我们可以直接指定图像的宽高以及渲染方式,例如:

BufferedImage resultImage = new BufferedImage(maxWidth, totalHeight, BufferedImage.TYPE_INT_RGB);

然后此篇主要介绍将图片拼接到一起的方式,这需要用到另外一个类Graphics2D,它提供对几何图形、坐标转换、颜色管理和文本布局的更复杂的控制。这是在Java(tm)平台上渲染二维形状、文本和图像的基本类。

先上代码:

/**
     * 拼接图片(垂直拼接,image在上,bufferedImage在下)
     * @param image
     * @param bufferedImage
     * @return
     */
    public static BufferedImage concatenateImagesVertically(BufferedImage image, BufferedImage bufferedImage) {
        //为适应图片,需要将拼接的两张图片的最大宽高取出
        //垂直拼接,方式为高度取二者之和,宽取二者中最大值
        //水平拼接,方式为宽度取二者之和,高取二者中最大值
        //以下为垂直拼接
        int totalHeight = () + ();
        int maxWidth = ((), ());
        //首先创建一个指定大小的空对象
        BufferedImage resultImage = new BufferedImage(maxWidth, totalHeight, BufferedImage.TYPE_INT_RGB);
        //然后将该对象转换为可被操作的Graphics2D对象
        Graphics2D g2d = ();
        //向该对象中拼入第一张图片
        (image, 0, 0, null);
        //向该对象拼入第二章图片
        (bufferedImage, 0, (), null);
        //最后处理该图像,此操作会释放占用的资源,之后将无法继续操作Graphics2D对象
        ();

        return resultImage;
    }

以上方法实现了将两个BufferedImage对象拼合到一起的过程,最后返回一个经过拼接的BufferedImage对象。

6、写在最后

        本文主要介绍了将一个PDF文件进行拆分并转储图片的流程,可以在一定程度上解决页面加载文件效率的问题。以上所用内容基本都在Java提供的API里面,详细研究可以参考Java源码。

附流程源码(传入文件下载连接,输出图片连接):

public String pdfDecompose(String pdfUrl) {
        try {
            InputStream inputStream = (pdfUrl);
            PDDocument document = (inputStream);
            int pageCount = ();
            BufferedImage bufferedImage = new BufferedImage(0,0, 1);
            for (int i = 0; i < pageCount; i++) {
                PDDocument newDocument = new PDDocument();
                PDPage page = (i);
                (page);
                //pdf转储图片文件
                PDFRenderer renderer = new PDFRenderer(newDocument);
                int numberOfPages = ();
                BufferedImage image = (numberOfPages, 1);
                concatenateImagesVertically(bufferedImage,image);
                ();
            }
            ();
            //TODO 上传返回
        } catch (IOException e) {
            ();
        }
        return null;
    }

    /**
     * 拼接图片(垂直拼接,image在上,bufferedImage在下)
     * @param image
     * @param bufferedImage
     * @return
     */
    public static BufferedImage concatenateImagesVertically(BufferedImage image, BufferedImage bufferedImage) {
        int totalHeight = () + ();
        int maxWidth = ((), ());

        BufferedImage resultImage = new BufferedImage(maxWidth, totalHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = ();

        (image, 0, 0, null);
        (bufferedImage, 0, (), null);

        ();

        return resultImage;
    }