系列文章:
Java 操作 Office:POI 之 word 图片处理
Java 操作 Office:POI word 之网络图片处理
一 POI简介
Apache POI - the Java API for Microsoft Documents。根据官网描述,poi是微软文档系列的Java API。这里的微软文档(Microsoft Documents),就是指word、excel(xls 和 xlsx)、PowerPoint 等。poi官网地址:https://poi.apache.org/。目前最新版本为 22年1月14日发布的POI5.2.0。发布记录可参见: RELEASE-NOTES;change log:changes;下载列表: https://archive.apache.org/dist/poi/release/bin/
二 POI组成
2.1 4.1.0与5.2.0版本组成
我们下载了4.1.0 和 5.2.0 两个版本的bin包,并解压后进行对比:
auxiliary,lib,ooxml-lib几个目录相同,内部依赖暂时不做对比。下面的jar包是我们开发时需要引入的依赖。 poi-${version}.jar, poi-examples-${version}.jar, poi-excelant-${version}.jar, poi-ooxml-${version}.jar, poi-scratchpad-${version}.jar,这几个是相同的。不同的地方是,4.1.0中的 poi-ooxml-schemas-${version}.jar,在5.2.0中变成了poi-ooxml-full-${version}.jar和poi-ooxml-lite-${version}.jar。
2.2 各jar包作用
在官网的components中有描述:Apache POI - Component Overview,这更详细的解释大家可以直接看原文内容:https://poi.apache.org/components/index.html。
下图是操作的文档类型与jar包之间的关系,通过这张表,可以明确当我们仅需要操作word或excel,以及2003或2007版本时,分别需要引入哪几个jar包(或maven依赖):
poi包内各jar包之间的依赖关系:
举个栗子:
只要操作excel,且是xls格式时,只需导入poi-version-yyyymmdd.jar。当我们还要使用xlsx格式、还要导入poi-ooxml-version-yyyymmdd.jar。至于poi-ooxml-schemas-version-yyyymmdd.jar这个jar基本不太会用到的。当我们需要操作word、ppt、viso、outlook等时需要用到poi-scratchpad-version-yyyymmdd.jar。
三 读取word内容
关于word的格式:.doc与.docx的介绍,可以参见这篇文章:What Is a .DOCX File, and How Is It Different from a .DOC File in Microsoft Word? 简单来说,相比.doc,docx中多余的“X”,代表Office Open XML标准。DOCX更高效,并且可以创建更小,更不易损坏的文件。 所以推荐尽可能使用DOCX, 仅当Word 2007之前的版本将使用该文件时,才需要DOC。
docx 是微软开发的基于 xml 的文字处理文件。docx 文件与 doc 文件不同, 因为 docx 文件将数据存储在压缩文件和文件夹中。早期版本的 microsoft office (office 2007之前) 不支持 docx 文件, 因为 docx 是基于 xml 的, 而之前的版本将doc文件存储为独立的二进制文件。
当我们选中一个docx文件,通过rar(windows),或The Unarchiver(mac)等解压软件执行解压后,会看到一个文件夹,内容如下:
可见是一系列xml文件(页眉页脚、styles、...)、theme(主题)、media(图片等多媒体文件)的集合。
四 读取word内容
在本文开始挂的系列文章链接中,已经包含了word文档创建、生成表格等相关操作,接下来我们要尝试读取word文档内容,最好包含格式,这样可以配合前端实现word在线编辑的效果,方便在web系统中集成;也可以考虑通过这种方式配合自然语言处理的相关模型/服务,实现word文档关键内容提取。
如大家所熟知,word目前也有.doc 和 .docx两种格式,对这两种格式的读取方式也稍有不同。接下来我们通过代码来详细阐述。
4.1 poi版本和依赖引入
我们使用的是4.1.0版本的poi,引入的依赖如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>test</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>project</artifactId> <properties> <java.version>1.8</java.version> <poi.version>4.1.0</poi.version> </properties> <dependencies> <!-- office 相关依赖jar包 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>${poi.version}</version> </dependency> </dependencies> </project>
4.2 docx文件内容读取
通过XWPFWordExtractor读取文档内容:
import org.apache.poi.ooxml.POIXMLProperties; import org.apache.poi.xwpf.extractor.XWPFWordExtractor; import org.apache.poi.xwpf.usermodel.XWPFDocument; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Date; public class ReadWordContent { public static void main(String[] args) throws Exception { String wordPath = "你的word文档地址.docx"; ReadWordContent readWordContent = new ReadWordContent(); readWordContent.readByExtractor(wordPath); } /** * 通过XWPFWordExtractor访问XWPFDocument的内容 * @throws Exception */ public void readByExtractor(String path) throws Exception { InputStream is = new FileInputStream(path); XWPFDocument doc = new XWPFDocument(is); XWPFWordExtractor extractor = new XWPFWordExtractor(doc); String text = extractor.getText(); System.out.println(text); /** * 打印文档信息 */ POIXMLProperties.CoreProperties coreProps = extractor.getCoreProperties(); this.printCoreProperties(coreProps); this.close(is); } /** * 输出CoreProperties信息 * @param coreProps */ private void printCoreProperties(POIXMLProperties.CoreProperties coreProps) { String category = coreProps.getCategory(); //分类 String creator = coreProps.getCreator(); //创建者,Microsoft Office User Date createdDate = coreProps.getCreated(); //创建时间 String title = coreProps.getTitle(); //标题 String description = coreProps.getDescription(); //描述,默认为null System.out.println(category); System.out.println(creator); System.out.println(createdDate); System.out.println(title); System.out.println(description); } /** * 关闭输入流 * @param is */ private void close(InputStream is) { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } }
4.3 doc文档内容及格式读取
import org.apache.commons.lang3.StringUtils; import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.model.StyleDescription; import org.apache.poi.hwpf.model.StyleSheet; import org.apache.poi.hwpf.usermodel.Paragraph; import org.apache.poi.hwpf.usermodel.Range; import java.io.*; /** * 注:HWPFDocument对应 word的.doc,不支持.docx */ public class ReadWordWithFormat { public static void main(String[] args) throws Exception { String filePath = "你的word文档地址.doc"; printWord(filePath); } public static void printWord(String filePath) throws IOException { InputStream is = new FileInputStream(filePath); HWPFDocument doc = new HWPFDocument(is); Range r = doc.getRange();// 文档范围 // System.out.println("段落数:"+r.numParagraphs()); for (int i = 0; i < r.numParagraphs(); i++) { Paragraph p = r.getParagraph(i);// 获取段落 int numStyles = doc.getStyleSheet().numStyles(); int styleIndex = p.getStyleIndex(); StyleSheet style_sheet = doc.getStyleSheet(); StyleDescription style = style_sheet.getStyleDescription(styleIndex); String styleName = style.getName(); // 打印各段落的格式 System.out.println(i+","+styleIndex+","+styleName); // String styleLoving = "标题"; // if (!StringUtils.isEmpty(styleName) && styleName.startsWith(styleLoving)){ // String text = p.text();// 段落文本 // System.out.println(text); // } } doc.close(); } }
在printWord()方法中,我们打印了文档的每个段落以及该段落的格式,以本地测试时使用的文档为例,输出如下:
0,0,正文 1,0,正文 2,1,标题 1 3,2,标题 2 4,0,正文 5,2,标题 2 6,0,正文 7,2,标题 2 8,0,正文 9,0,正文 10,0,正文 11,2,标题 2 12,0,正文 13,1,标题 1 14,2,标题 2 15,0,正文 16,0,正文 17,2,标题 2 18,0,正文 19,1,标题 1