Java准确获取Word/Excel/PPT/PDF的页数(附Word页数读不准的处理办法)

news/2024/7/21 3:56:36 标签: java, poi, excel, word, ppt

Java准确获取Word/Excel/PPT/PDF的页数(附Word页数读不准的处理办法)

  • 1.需求背景
  • 2.环境准备工作
    • 2.1 JACOB介绍及安装
    • 2.2 Microsoft Office Word的设置
  • 3.代码
    • 3.1 代码示例
      • 3.1.1 pom.xml
      • 3.1.2 主要功能实现
      • 3.1.3 Main方法
      • 3.1.4 运行结果
    • 3.2 注意事项
      • 3.2.1 获取Excel页数的注意事项
      • 3.2.2 获取Word页数的注意事项
      • 3.2.3 获取PPT页数的注意事项

1.需求背景

前两天接了个小需求,就是用Java准确地判断出Word(.doc和.docx)、Excel(.xls和.xlsx)、PPT(.ppt和.pptx)还有PDF的页数。开始觉得很简单,想都没想就用了Apache的POI,但后来发现个大问题:
Apache POI对于.doc(Office Word 1997- 2003的版本)后缀的word文件,页数读取不准!
度娘了一下,发现大家普遍都有这个问题,解决办法也是五花八门,有用POI的,有用JACOB将Word转PDF的,还有用第三方工具比如PageOffice/OpenOffice的,但是感觉都不是特别简洁。所以结合这些博客的经验及我自己的摸索,我终于找到了较为简单准确的解决方式,下面完整地分享给大家。

2.环境准备工作

2.1 JACOB介绍及安装

我的思路还是使用JACOB来操作Word,因为对于Office Word 97-03这种远古版本,除了软件自身提供的宏,似乎没有什么能对它进行直接的操作,而在Windows平台为了解决这种软件缺乏通用API的问题,推出了COM的解决方案。
而想通过Java来操作DOM,我们就需要一个JACOB(Java-COM Bridge)这样的桥梁。JACOB 开源项目提供的是一个 JVM 独立的自动化服务器实现,其核心是基于 JNI 技术实现的 Variant, Dispatch 等接口,从而调用Windows的COM(Component Object Model组对象模型)。
JACOB下载地址为:https://sourceforge.net/projects/jacob-project/
如下图:
在这里插入图片描述
点击Download,然后解压,将其中的jacob-1.19-x64.dll复制到System32文件夹下,如下图:
在这里插入图片描述

2.2 Microsoft Office Word的设置

因为是调用的Word Application自身,所以环境里必须得安装有Office Word,这个就不赘述。
Word里需要做如下两项设置,如下图:
在这里插入图片描述
在这里插入图片描述
原因是如果使用高版本的Office Word打开.doc文件,默认是预览视图,而不是编辑视图,这个预览视图会影响后面要介绍的方法的判断准确性,如下图:
在这里插入图片描述

3.代码

3.1 代码示例

3.1.1 pom.xml

java"><dependencies>
        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna-platform</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>3.8</version>
        </dependency>
        <!--用于操作Office系列-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.8</version>
        </dependency>
        <!--用于操作Word-->
        <!-- https://mvnrepository.com/artifact/net.sf.jacob-project/jacob -->
        <dependency>
            <groupId>net.sf.jacob-project</groupId>
            <artifactId>jacob</artifactId>
            <version>1.14.3</version>
        </dependency>
        <!--用于操作PDF-->
        <!-- https://mvnrepository.com/artifact/com.lowagie/itext -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.0.6</version>
        </dependency>
    </dependencies>

3.1.2 主要功能实现

java">word">package com.docreader;
word">import com.itextpdf.text.pdf.PdfReader;
word">import com.jacob.activeX.ActiveXComponent;
word">import com.jacob.com.Dispatch;
word">import com.jacob.com.Variant;
word">import org.apache.poi.POIXMLDocument;
word">import org.apache.poi.hslf.HSLFSlideShow;
word">import org.apache.poi.hslf.usermodel.SlideShow;
word">import org.apache.poi.hssf.usermodel.HSSFWorkbook;
word">import org.apache.poi.xslf.usermodel.XMLSlideShow;
word">import org.apache.poi.xssf.usermodel.XSSFWorkbook;
word">import org.apache.poi.xwpf.usermodel.XWPFDocument;
word">import java.io.FileInputStream;
word">import java.io.IOException;
word">public word">class reader {
    /**
     * description: 静态方法,用于判断文件类型,并返回页数
     * @param filePath 文件完整路径
     */
    word">public word">static word">int getFilePageNum(String filePath) word">throws IOException {
        word">int pageNum = 0;
        String lowerFilePath = filePath.toLowerCase();
        word">if (lowerFilePath.endsWith(".xls")) {
            HSSFWorkbook workbook = word">new HSSFWorkbook(word">new FileInputStream(lowerFilePath));
            Integer sheetNums = workbook.getNumberOfSheets();
            word">if (sheetNums > 0) {
                pageNum = workbook.getSheetAt(0).getRowBreaks().length + 1;
            }
        } word">else word">if (lowerFilePath.endsWith(".xlsx")) {
            XSSFWorkbook xwb = word">new XSSFWorkbook(lowerFilePath);
            Integer sheetNums = xwb.getNumberOfSheets();
            word">if (sheetNums > 0) {
                pageNum = xwb.getSheetAt(0).getRowBreaks().length + 1;
            }
        } word">else word">if (lowerFilePath.endsWith(".docx")) {
            XWPFDocument docx = word">new XWPFDocument(POIXMLDocument.openPackage(lowerFilePath));
            pageNum = docx.getProperties().getExtendedProperties().getUnderlyingProperties().getPages();
        } word">else word">if (lowerFilePath.endsWith(".doc")) {
            //下方的方法不好使,经常只统计出一页
//            HWPFDocument wordDoc = new HWPFDocument(new FileInputStream(lowerFilePath));
//            pageNum = wordDoc.getSummaryInformation().getPageCount();
            //采用如下方法
            pageNum = getDocPageNum(lowerFilePath);
        } word">else word">if (lowerFilePath.endsWith(".ppt")) {
            HSLFSlideShow document = word">new HSLFSlideShow(word">new FileInputStream(lowerFilePath));
            SlideShow slideShow = word">new SlideShow(document);
            pageNum = slideShow.getSlides().length;
        } word">else word">if (lowerFilePath.endsWith(".pptx")) {
            XMLSlideShow xslideShow = word">new XMLSlideShow(word">new FileInputStream(lowerFilePath));
            pageNum = xslideShow.getSlides().length + 1;
        } word">else word">if (lowerFilePath.endsWith(".pdf")){
            PdfReader reader = word">new PdfReader(filePath);
            pageNum = reader.getNumberOfPages();
        }
        word">return pageNum;
    }
    /**
     * description: 静态方法,专门用于判断Office 2003版本之前的Word(格式为.doc)的页数
     * @param filePath 文件完整路径
     */
    word">private word">static word">int getDocPageNum(String filePath) {
        word">int pageNum = 0;
        word">try{
            // 建立ActiveX部件
            ActiveXComponent wordCom = word">new ActiveXComponent("Word.Application");
            //word应用程序不可见
            wordCom.setProperty("Visible", false);
            // 返回wrdCom.Documents的Dispatch
            Dispatch wrdDocs = wordCom.getProperty("Documents").toDispatch();//Documents表示word的所有文档窗口(word是多文档应用程序)
            // 调用wrdCom.Documents.Open方法打开指定的word文档,返回wordDoc
            Dispatch wordDoc = Dispatch.call(wrdDocs, "Open", filePath, false, true, false).toDispatch();
            Dispatch selection = Dispatch.get(wordCom, "Selection").toDispatch();
            pageNum = Integer.parseInt(Dispatch.call(selection,"information",4).toString());//总页数 //显示修订内容的最终状态
            //关闭文档且不保存
            Dispatch.call(wordDoc, "Close", word">new Variant(false));
            //退出进程对象
            wordCom.invoke("Quit", word">new Variant[] {});
        } word">catch (Exception e) {
            e.printStackTrace();
        }
        word">return pageNum;
    }
}

3.1.3 Main方法

java">word">import com.docreader.reader;
word">import javax.imageio.ImageIO;
word">import java.awt.*;
word">import java.awt.event.KeyEvent;
word">import java.awt.image.BufferedImage;
word">import java.io.File;
word">public word">class Main {
    word">public word">static word">void main(String[] args) {
        word">try{
            word">int xlsNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\5 pages.xls");
            System.out.println("Office2003之前版本的Excel:5 pages.xls 的页数为:" + xlsNum);
            word">int xlsxNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\8 pages.xlsx");
            System.out.println("Office2003之后版本的Excel:8 pages.xlsx 的页数为:" + xlsxNum);
            word">int docNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\30 pages.doc");
            System.out.println("Office2003之前版本的Word:30 pages.doc 的页数为:" + docNum);
            word">int docxNum =  reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\133 pages.docx");
            System.out.println("Office2003之后版本的Word:133 pages.docx 的页数为:" + docxNum);
            word">int pptNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\35 pages.ppt");
            System.out.println("Office2003之前版本的PPT:35 pages.ppt 的页数为:" + pptNum);
            word">int pptxNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\95 pages.pptx");
            System.out.println("Office2003之后版本的PPT:95 pages.pptx 的页数为:" + pptxNum);
            word">int pdfNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\30 pages.pdf");
            System.out.println("PDF:30 pages.pdf 的页数为:" + pdfNum);
        } word">catch (Exception e){
            e.printStackTrace();
        }
    }
}

3.1.4 运行结果

在这里插入图片描述
在这里插入图片描述

3.2 注意事项

3.2.1 获取Excel页数的注意事项

java">word">if (lowerFilePath.endsWith(".xls")) {
            HSSFWorkbook workbook = word">new HSSFWorkbook(word">new FileInputStream(lowerFilePath));
            Integer sheetNums = workbook.getNumberOfSheets();
            word">if (sheetNums > 0) {
                pageNum = workbook.getSheetAt(0).getRowBreaks().length + 1;
            }
        } word">else word">if (lowerFilePath.endsWith(".xlsx")) {
            XSSFWorkbook xwb = word">new XSSFWorkbook(lowerFilePath);
            Integer sheetNums = xwb.getNumberOfSheets();
            word">if (sheetNums > 0) {
                pageNum = xwb.getSheetAt(0).getRowBreaks().length + 1;
            }
        }

仔细看代码,这里的:

java">getRowBreaks().length + 1

指的是Excel中的分页符的数量,如下图:
在这里插入图片描述
如果Excel中插入了7个分页符,那么总共可打印的页数就是7+1 = 8页,如下图:
在这里插入图片描述
注意,我看有的教程里,页数 = workbook.getNumberOfSheets(),这就错得太离谱了,这是sheet的数量,根本不是页数。

3.2.2 获取Word页数的注意事项

java">/**
     * description: 静态方法,专门用于判断Office 2003版本之前的Word(格式为.doc)的页数
     * @param filePath 文件完整路径
     */
    word">private word">static word">int getDocPageNum(String filePath) {
        word">int pageNum = 0;
        word">try{
            // 建立ActiveX部件
            ActiveXComponent wordCom = word">new ActiveXComponent("Word.Application");
            //word应用程序不可见
            wordCom.setProperty("Visible", false);
            // 返回wrdCom.Documents的Dispatch
            Dispatch wrdDocs = wordCom.getProperty("Documents").toDispatch();//Documents表示word的所有文档窗口(word是多文档应用程序)
            // 调用wrdCom.Documents.Open方法打开指定的word文档,返回wordDoc
            Dispatch wordDoc = Dispatch.call(wrdDocs, "Open", filePath, false, true, false).toDispatch();
            Dispatch selection = Dispatch.get(wordCom, "Selection").toDispatch();
            pageNum = Integer.parseInt(Dispatch.call(selection,"information",4).toString());//总页数 //显示修订内容的最终状态
            //关闭文档且不保存
            Dispatch.call(wordDoc, "Close", word">new Variant(false));
            //退出进程对象
            wordCom.invoke("Quit", word">new Variant[] {});
        } word">catch (Exception e) {
            e.printStackTrace();
        }
        word">return pageNum;
    }

这里的:

java">//word应用程序不可见
wordCom.setProperty("Visible", false);

如果设置成true,则会打开可见的Word应用,并打开指定的Word文档,对于调试时会有很大帮助。

3.2.3 获取PPT页数的注意事项

java">word">else word">if (lowerFilePath.endsWith(".pptx")) {
            XMLSlideShow xslideShow = word">new XMLSlideShow(word">new FileInputStream(lowerFilePath));
            pageNum = xslideShow.getSlides().length + 1;
        }

仔细看代码,这里需要+1。具体原因未知,但是我经过多次测试,结果是准确的。


http://www.niftyadmin.cn/n/1870119.html

相关文章

java 虚拟机工作原理详解

笔记&#xff1a;本文主要讲述类加载的过程和 jvm 执行一个函数时 jvm 运行时数据区中 java 栈数据交换的详细过程 一、类加载器 首先来看一下java程序的执行过程。 从这个框图很容易大体上了解java程序工作原理。首先&#xff0c;你写好java代码&#xff0c;保存到硬盘当中。然…

The openssl notes

openssl version 查看版本号openssl dgst 消息摘要 消息摘要&#xff0c;所采用的命令是通过选项的形式实现的&#xff0c;消息摘要的输入可以是stdin,或者文件。 比如&#xff1a;openssl dgst -sha1 test.txt 当然&#xff0c;也可以直接用命令&#xff0c;省掉dgst。例如&am…

Apikit 自学日记:团队管理

团队管理 一、工作空间管理 工作空间类似于部门或公司的概念&#xff0c;能帮助您更好地管理团队。 1.1 创建空间 点击页面左上角功能菜单&#xff0c;在下拉菜单中选择要切换的工作空间。 点击创建/加入&#xff0c;在引导页面中选择创建工作空间&#xff0c;填写工作空间…

java 虚拟机的研究与实现

2006-9-25 11:08:12 作者&#xff1a;夏兵 俞建军(79) 引言 Java虚拟机本质是就是一个程序&#xff0c;当它在命令行上启动的时候&#xff0c;就开始执行保存在某字节码文件中的指令。Java语言的可移植性正是建立在Java虚拟机的基础上。任何平台只要装有针对于该平台的Java虚…

RocketMQ吐血总结

RocketMQ吐血总结 架构 概念模型 最基本的概念模型与扩展后段概念模型 存储模型 RocketMQ吐血总结 User Guide RocketMQ是一款分布式消息中间件&#xff0c;最初是由阿里巴巴消息中间件团队研发并大规模应用于生产系统&#xff0c;满足线上海量消息堆积的需求&#xff0c; 在2…

Introduction to cryptography

cryptography & encription cryptography 和 encription都是指加密&#xff0c;密码&#xff0c;但是这两个概念使用起来有点差异。 cryptography是“研究秘密写的科学”&#xff0c;是指安全系统&#xff0c;密码系统&#xff0c;概念的范围更大。 encription是加密&am…

RocketMQ事务消息学习及刨坑过程

一、背景 MQ组件是系统架构里必不可少的一门利器&#xff0c;设计层面可以降低系统耦合度&#xff0c;高并发场景又可以起到削峰填谷的作用&#xff0c;从单体应用到集群部署方案&#xff0c;再到现在的微服务架构&#xff0c;MQ凭借其优秀的性能和高可靠性&#xff0c;得到了…

Java三大框架的技术起源

在Java开发中&#xff0c;我们经常使用Struts、Hibernate和Spring三个主流框架&#xff0c;但你是否知道这三个框架最初是为解决怎样的问题而生的&#xff1f;Struts、Hibernate和Spring是我们Java开发中的常用关键&#xff0c;他们分别针对不同的应用场景给出最合适的解决方案…