Java 拆分 Word 文档教程:按页、分页符和分节符拆分

文章来源声明: 原文作者:用户372157426135; 来源站点:掘金; 原文链接:https://juejin.cn/post/7686464571943059497; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

三种拆分方式覆盖多数文档处理场景,明确的索引与页数说明能避免常见误区,适合需要批量拆分 Word 的 Java 开发者参考。

在文档处理场景中,将一个 Word 文件拆分成多个独立文档是一项常见需求。例如,生成单独的章节文件、提取指定页面内容,或将大型报告按结构拆分保存,都需要对原始文档进行精准分割。

Java 提供了多种方式处理 Word 文档拆分操作,可以根据不同需求选择合适的拆分依据:按页拆分能够保留文档的页面布局,按分页符拆分适用于人工设置的内容分隔,而按分节符拆分则更适合处理具有章节结构的复杂文档。

本文将介绍如何使用 Java 实现 Word 文档的按页拆分、按分页符拆分以及按分节符拆分,帮助开发者根据实际业务需求灵活处理 Word 文件。

环境设置

要运行下面的代码示例,需要先在 Java 项目 中添加 Word 文档处理所需的依赖。

如果使用 Maven,可以在 ​​pom.xml​​ 中加入:

<span><<span>repositories</span>></span>
    <span><<span>repository</span>></span>
        <span><<span>id</span>></span>com.e-iceblue<span></<span>id</span>></span>
        <span><<span>name</span>></span>e-iceblue<span></<span>name</span>></span>
        <span><<span>url</span>></span>https://repo.e-iceblue.cn/repository/maven-public/<span></<span>url</span>></span>
    <span></<span>repository</span>></span>
<span></<span>repositories</span>></span>

<span><<span>dependencies</span>></span>
    <span><<span>dependency</span>></span>
        <span><<span>groupId</span>></span>e-iceblue<span></<span>groupId</span>></span>
        <span><<span>artifactId</span>></span>spire.doc<span></<span>artifactId</span>></span>
        <span><<span>version</span>></span>14.8.4<span></<span>version</span>></span>
    <span></<span>dependency</span>></span>
<span></<span>dependencies</span>></span>

  1. 将 Word 文档的每一页拆分为单独文件

如果需要按照 Word 实际排版后的页面进行拆分,可以使用 ​​Document.extractPages()​​ 方法从原文档中提取页面,并生成新的 ​​Document​​ 对象。

下面的示例获取 Word 文档的总页数,然后逐页提取并保存为独立的 DOCX 文件:

<span>import</span> com.<span>spire</span>.<span>doc</span>.<span>Document</span>;
<span>import</span> com.<span>spire</span>.<span>doc</span>.<span>FileFormat</span>;

<span>public</span> <span>class</span> <span>SplitWordByPage</span> {
    <span>public</span> <span>static</span> <span>void</span> <span>main</span>(<span><span>String</span>[] args</span>) {

        <span>// Load the Word document</span>
        <span>Document</span> <span>document</span> = <span>new</span> <span>Document</span>();
        <span>document</span>.<span>loadFromFile</span>(<span>"Sample.docx"</span>);

        <span>// Get the total number of pages</span>
        int pageCount = <span>document</span>.<span>getPageCount</span>();

        <span>// Extract each page to a separate document</span>
        <span>for</span> (int i = <span>0</span>; i < pageCount; i++) {

            <span>Document</span> pageDocument = <span>document</span>.<span>extractPages</span>(i, <span>1</span>);

            pageDocument.<span>saveToFile</span>(
                    <span>"output/Page-"</span> + (i + <span>1</span>) + <span>".docx"</span>,
                    <span>FileFormat</span>.<span>Docx</span>
            );

            pageDocument.<span>close</span>();
        }

        <span>document</span>.<span>close</span>();
    }
}

例如,一个包含 5 页的 Word 文档会被拆分为:

Page-1.docx
Page-2.docx
Page-3.docx
Page-4.docx
Page-5.docx

​extractPages()​​ 的第一个参数表示起始页面索引,从 ​​0​​ 开始;第二个参数表示需要提取的页面数量。因此:

document<span>.extractPages</span>(<span>i</span>, <span>1</span>);

表示从索引 ​​i​​ 开始提取 1 页。

  1. 提取 Word 文档中的指定页码范围

​extractPages()​​ 也可以一次提取连续的多个页面。

例如,需要将原文档的第 3 页到第 6 页保存为一个新的 Word 文档,可以使用下面的代码:

<span>import</span> com.<span>spire</span>.<span>doc</span>.<span>Document</span>;
<span>import</span> com.<span>spire</span>.<span>doc</span>.<span>FileFormat</span>;

<span>public</span> <span>class</span> <span>ExtractWordPageRange</span> {
    <span>public</span> <span>static</span> <span>void</span> <span>main</span>(<span><span>String</span>[] args</span>) {

        <span>// Load the Word document</span>
        <span>Document</span> <span>document</span> = <span>new</span> <span>Document</span>();
        <span>document</span>.<span>loadFromFile</span>(<span>"Sample.docx"</span>);

        int startPage = <span>3</span>;
        int endPage = <span>6</span>;

        <span>// Validate the page range</span>
        <span>if</span> (startPage < <span>1</span>
                || endPage < startPage
                || endPage > <span>document</span>.<span>getPageCount</span>()) {
            <span>throw</span> <span>new</span> <span>IllegalArgumentException</span>(<span>"Invalid page range."</span>);
        }

        <span>// Convert the page number to a zero-based index</span>
        int startIndex = startPage - <span>1</span>;

        <span>// Calculate the number of pages to extract</span>
        int pageCount = endPage - startPage + <span>1</span>;

        <span>// Extract the specified pages</span>
        <span>Document</span> extractedDocument =
                <span>document</span>.<span>extractPages</span>(startIndex, pageCount);

        extractedDocument.<span>saveToFile</span>(
                <span>"output/Pages-3-6.docx"</span>,
                <span>FileFormat</span>.<span>Docx</span>
        );

        extractedDocument.<span>close</span>();
        <span>document</span>.<span>close</span>();
    }
}

需要注意的是,​​extractPages()​​ 使用从 ​​0​​ 开始的页面索引,而且第二个参数是提取页数,不是结束页码。

因此,提取第 3 页到第 6 页时实际调用的是:

document.extractPages(2, 4)<span>;</span>

  1. 按分页符拆分 Word 文档

分页符通常用于强制后续内容从新的一页开始。在 Word 中通过 Ctrl + Enter 插入的分页符属于显式分页符。

如果希望根据这些分页符拆分文档,可以遍历段落中的 ​​Break​​ 对象,并通过 ​​BreakType.Page_Break​​ 判断是否遇到了分页符。

下面的示例在检测到分页符时结束当前文档,并将后续内容写入新的 Word 文件:

import com<span>.spire</span><span>.doc</span>.*;
import com<span>.spire</span><span>.doc</span><span>.documents</span>.*;

public class SplitWordByPageBreak {

    public static void <span>main</span>(String[] args) {

        <span>// Load the source document</span>
        Document source = new <span>Document</span>();
        source<span>.loadFromFile</span>("Sample.docx");

        <span>// Create the first output document</span>
        Document partDocument = <span>createDocument</span>(source);
        <span>Section</span> targetSection = partDocument<span>.getSections</span>()<span>.get</span>(<span>0</span>);

        int fileIndex = <span>1</span>;

        <span>// Traverse all sections</span>
        for (int s = <span>0</span>; s < source.getSections()<span>.getCount</span>(); s++) {

            <span>Section</span> sourceSection = source<span>.getSections</span>()<span>.get</span>(s);

            <span>// Copy section properties</span>
            sourceSection<span>.cloneSectionPropertiesTo</span>(targetSection);

            <span>// Traverse paragraphs and tables</span>
            for (int i = <span>0</span>;
                 i < sourceSection.getBody()<span>.getChildObjects</span>()<span>.getCount</span>();
                 <span>i</span>++) {

                DocumentObject <span>object</span> =
                        sourceSection<span>.getBody</span>()
                                <span>.getChildObjects</span>()
                                <span>.get</span>(i);

                if (object instanceof Table) {

                    targetSection<span>.getBody</span>()
                            <span>.getChildObjects</span>()
                            <span>.add</span>(object.deepClone());

                } else if (object instanceof Paragraph) {

                    Paragraph paragraph = (Paragraph) <span>object</span>;

                    targetSection<span>.getBody</span>()
                            <span>.getChildObjects</span>()
                            <span>.add</span>(paragraph.deepClone());

                    <span>// Check for page breaks</span>
                    for (int j = <span>0</span>;
                         j < paragraph.getChildObjects()<span>.getCount</span>();
                         j++) {

                        DocumentObject child =
                                paragraph<span>.getChildObjects</span>()<span>.get</span>(j);

                        if (child instanceof Break
                                && ((Break) child)<span>.getBreakType</span>()
                                <span>.equals</span>(BreakType.Page_Break)) {

                            int breakIndex =
                                    paragraph<span>.getChildObjects</span>()
                                            <span>.indexOf</span>(child);

                            <span>// Remove the page break from the current output</span>
                            Paragraph outputParagraph =
                                    targetSection<span>.getBody</span>()
                                            <span>.getLastParagraph</span>();

                            outputParagraph<span>.getChildObjects</span>()
                                    <span>.removeAt</span>(breakIndex);

                            <span>// Save the current part</span>
                            partDocument<span>.saveToFile</span>(
                                    "output/Part-" + fileIndex + ".docx",
                                    FileFormat.Docx
                            );

                            partDocument<span>.close</span>();
                            fileIndex++;

                            <span>// Create the next document</span>
                            partDocument = <span>createDocument</span>(source);
                            targetSection =
                                    partDocument<span>.getSections</span>()<span>.get</span>(<span>0</span>);

                            sourceSection<span>.cloneSectionPropertiesTo</span>(
                                    targetSection
                            );

                            <span>// Copy the paragraph after the page break</span>
                            targetSection<span>.getBody</span>()
                                    <span>.getChildObjects</span>()
                                    <span>.add</span>(paragraph.deepClone());

                            Paragraph firstParagraph =
                                    targetSection<span>.getParagraphs</span>()<span>.get</span>(<span>0</span>);

                            <span>// Remove the page break and content before it</span>
                            while (breakIndex >= <span>0</span>
                                    && firstParagraph.getChildObjects()
                                            <span>.getCount</span>() > <span>0</span>) {

                                firstParagraph<span>.getChildObjects</span>()
                                        <span>.removeAt</span>(breakIndex);

                                breakIndex--;
                            }

                            if (firstParagraph.getChildObjects()
                                    <span>.getCount</span>() == <span>0</span>) {

                                targetSection<span>.getBody</span>()
                                        <span>.getChildObjects</span>()
                                        <span>.remove</span>(firstParagraph);
                            }
                        }
                    }
                }
            }
        }

        <span>// Save the last part</span>
        partDocument<span>.saveToFile</span>(
                "output/Part-" + fileIndex + ".docx",
                FileFormat.Docx
        );

        partDocument<span>.close</span>();
        source<span>.close</span>();
    }

    private static Document <span>createDocument</span>(Document source) {

        Document document = new <span>Document</span>();

        source<span>.cloneDefaultStyleTo</span>(document);
        source<span>.cloneThemesTo</span>(document);
        source<span>.cloneCompatibilityTo</span>(document);

        document<span>.addSection</span>();

        return document;
    }
}

如果原文档中包含两个分页符,拆分后会得到:

Part-1.docx
Part-2.docx
Part-3.docx

这里识别的是文档中实际存在的 Page Break。文字因页面空间不足而自动流到下一页并不属于分页符,因此不会触发拆分。

如果需要按照 Word 最终显示的每一页拆分文档,应使用前面的 ​​extractPages()​​ 方法。

  1. 按分节符拆分 Word 文档

Word 中的分节符会将文档划分为多个 Section。不同 Section 可以具有独立的页面尺寸、页边距、页眉页脚和页面方向等设置。

在 Spire.Doc for Java 中,可以直接遍历 ​​Document.getSections()​​,将每个 Section 克隆到新的 Word 文档中。

<span>import</span> com.<span>spire</span>.<span>doc</span>.<span>Document</span>;
<span>import</span> com.<span>spire</span>.<span>doc</span>.<span>FileFormat</span>;
<span>import</span> com.<span>spire</span>.<span>doc</span>.<span>Section</span>;

<span>public</span> <span>class</span> <span>SplitWordBySectionBreak</span> {
    <span>public</span> <span>static</span> <span>void</span> <span>main</span>(<span><span>String</span>[] args</span>) {

        <span>// Load the Word document</span>
        <span>Document</span> <span>document</span> = <span>new</span> <span>Document</span>();
        <span>document</span>.<span>loadFromFile</span>(<span>"Sample.docx"</span>);

        <span>// Traverse all sections</span>
        <span>for</span> (int i = <span>0</span>;
             i < <span>document</span>.<span>getSections</span>().<span>getCount</span>();
             i++) {

            <span>Section</span> sourceSection =
                    <span>document</span>.<span>getSections</span>().<span>get</span>(i);

            <span>// Create a new document</span>
            <span>Document</span> sectionDocument = <span>new</span> <span>Document</span>();

            <span>// Preserve document-level styles and settings</span>
            <span>document</span>.<span>cloneDefaultStyleTo</span>(sectionDocument);
            <span>document</span>.<span>cloneThemesTo</span>(sectionDocument);
            <span>document</span>.<span>cloneCompatibilityTo</span>(sectionDocument);

            <span>// Clone the current section</span>
            sectionDocument.<span>getSections</span>()
                    .<span>add</span>(sourceSection.<span>deepClone</span>());

            <span>// Save it as a separate Word file</span>
            sectionDocument.<span>saveToFile</span>(
                    <span>"output/Section-"</span> + (i + <span>1</span>) + <span>".docx"</span>,
                    <span>FileFormat</span>.<span>Docx</span>
            );

            sectionDocument.<span>close</span>();
        }

        <span>document</span>.<span>close</span>();
    }
}

如果原始文档包含三个 Section,拆分后会得到:

<span>Section</span>-<span>1</span><span>.docx</span>
<span>Section</span>-<span>2</span><span>.docx</span>
<span>Section</span>-<span>3</span><span>.docx</span>

一个 Section 可以包含一页,也可以包含多页。因此,按分节符拆分并不等同于按页拆分,而是保留每个 Section 中包含的全部内容。

Word 文档拆分方式对比

拆分方式拆分依据核心实现
每页拆分Word 实际页面​`​extractPages(i, 1)​`​
指定页码范围连续的实际页面​`​extractPages(index, count)​`​
按分页符拆分显式分页符​`​BreakType.Page_Break​`​
按分节符拆分Word Section​`​Section.deepClone()​`​

如果需要根据 Word 最终排版结果拆分页面,可以使用 ​​extractPages()​​;如果文档已经通过分页符或分节符划分内容,则可以直接按照相应的文档结构进行拆分。