杰瑞科技汇

Java如何将HTML转为Word文档?

核心思路

无论使用哪种库,其基本原理都是相似的:

  1. 解析 HTML:将 HTML 字符串或文件解析成一个内存中的文档对象模型。
  2. 生成 Word:遍历这个 DOM,并将其内容、样式、图片等元素,按照 Word 文档的格式要求(如 .docx 基于 OpenXML 格式)重新组织和写入。

使用 Apache POI (纯 Java 方案)

Apache POI 是 Java 操作 Office 文件最著名的库,虽然它本身不直接支持 HTML 转换,但我们可以结合一个强大的辅助库 jsoup 来实现。

原理

  • jsoup:用于解析 HTML,它能非常方便地提取文本、链接、图片以及 CSS 样式。
  • Apache POI:用于创建 .docx 文件,我们将使用 XWPF (XML Word Processing Format) API 来构建 Word 文档的段落、表格、图片等。

优点

  • 纯 Java 实现:无需安装任何外部软件或依赖。
  • 功能强大:POI 提供了对 Word 文件格式的底层控制,可以高度定制 Word 的样式和结构。
  • 免费开源:Apache 2.0 许可证,商业使用友好。

缺点

  • 代码量较大:需要手动处理 HTML 元素到 Word 元素的映射,实现一个完善的转换器需要编写较多代码。
  • 样式转换复杂:将 CSS 样式(如 margin, padding, display: flex)精确地转换为 POI 的样式(如段落间距、边框)是一个挑战。

Maven 依赖

<dependencies>
    <!-- Apache POI for .docx -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.3</version>
    </dependency>
    <!-- jsoup for HTML parsing -->
    <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.15.3</version>
    </dependency>
</dependencies>

代码示例

这个示例将一个简单的 HTML 字符串转换成 Word 文档,并处理了文本加粗、段落和图片。

import org.apache.poi.xwpf.usermodel.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class HtmlToWordWithPoi {
    public static void main(String[] args) {
        String htmlContent = "<h1>Hello, Apache POI!</h1>" +
                "<p>This is a <b>paragraph</b> with some <i>italic</i> text.</p>" +
                "<p>Here is a list:</p>" +
                "<ul><li>Item 1</li><li>Item 2</li></ul>" +
                "<p>And an image:</p>" +
                "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==\">";
        String outputPath = "output_poi.docx";
        try {
            convertHtmlToWord(htmlContent, outputPath);
            System.out.println("Word 文档生成成功: " + outputPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void convertHtmlToWord(String html, String outputPath) throws Exception {
        // 1. 使用 jsoup 解析 HTML
        Document doc = Jsoup.parse(html);
        Element body = doc.body();
        // 2. 创建 Word 文档
        try (XWPFDocument wordDocument = new XWPFDocument();
             FileOutputStream out = new FileOutputStream(outputPath)) {
            // 3. 遍历 HTML 元素并添加到 Word
            for (Element element : body.children()) {
                if (element.tagName().equalsIgnoreCase("h1")) {
                    XWPFParagraph p = wordDocument.createParagraph();
                    p.setAlignment(ParagraphAlignment.CENTER);
                    XWPFRun r = p.createRun();
                    r.setBold(true);
                    r.setFontSize(20);
                    r.setText(element.text());
                } else if (element.tagName().equalsIgnoreCase("p")) {
                    XWPFParagraph p = wordDocument.createParagraph();
                    XWPFRun r = p.createRun();
                    r.setText(element.text());
                    // 简单处理加粗和斜体
                    for (Element child : element.children()) {
                        if (child.tagName().equalsIgnoreCase("b")) {
                            r.setBold(true);
                            r.setText(child.text());
                            r = p.createRun(); // 创建新的 Run 以重置样式
                        } else if (child.tagName().equalsIgnoreCase("i")) {
                            r.setItalic(true);
                            r.setText(child.text());
                            r = p.createRun();
                        }
                    }
                } else if (element.tagName().equalsIgnoreCase("img")) {
                    String src = element.attr("src");
                    if (src.startsWith("data:image/")) {
                        // 处理 Base64 图片
                        String base64Data = src.split(",")[1];
                        byte[] imageBytes = java.util.Base64.getDecoder().decode(base64Data);
                        XWPFParagraph p = wordDocument.createParagraph();
                        p.setAlignment(ParagraphAlignment.CENTER);
                        XWPFRun r = p.createRun();
                        r.addPicture(new ByteArrayInputStream(imageBytes), XWPFDocument.PICTURE_TYPE_PNG, "image.png", Units.toEMU(200), Units.toEMU(200));
                    }
                }
                // ... 可以继续添加对 ul, ol, table 等标签的处理
            }
            // 4. 保存文档
            wordDocument.write(out);
        }
    }
}

使用 Flying Saucer (xhtmlrenderer) + POI

这是一个非常经典和强大的组合,尤其擅长处理带样式的 HTML。

原理

  • Flying Saucer:一个 XHTML/CSS 渲染引擎,它可以将一个 XHTML 文件渲染成一个包含所有样式信息的 Document Object Model,这个 DOM 可以被导出为图片(如 PNG)或直接转换为可编辑的 Word 文档格式。
  • Apache POI:在 Flying Saucer 的帮助下,将渲染后的内容写入 .docx 文件,Flying Saucer 甚至提供了 DocxRenderer,可以直接将 HTML 转换为 POI 的 XWPFDocument 对象。

优点

  • 优秀的 CSS 支持:能很好地保留 HTML 中的 CSS 样式,这是 POI + jsoup 组合难以比拟的。
  • 转换质量高:生成的 Word 文档在视觉上与原始 HTML 页面非常接近。
  • 成熟稳定:是一个老牌项目,广泛用于网页截图和文档转换。

缺点

  • 性能开销:渲染引擎需要处理 CSS 布局,比简单的字符串解析要慢。
  • 依赖复杂:需要 Flying Saucer 和 POI 两个库。
  • 非纯文本:如果目标是生成一个可编辑的 Word 文档,Flying Saucer 通常会将内容“扁平化”为图片或不可编辑的格式,失去可编辑性。但对于生成最终版、排版精美的报告文档,这是最佳选择之一。

Maven 依赖

<dependencies>
    <!-- Flying Saucer -->
    <dependency>
        <groupId>org.xhtmlrenderer</groupId>
        <artifactId>flying-saucer-pdf</artifactId>
        <version>9.1.22</version>
    </dependency>
    <!-- Or use flying-saucer-pdf-itext5 for PDF, or flying-saucer-docx for Word -->
    <!-- 注意:flying-saucer-docx 可能不是官方维护的核心包,社区方案更常见的是用 FS 生成内容,再用 POI 写入 -->
    <!-- 这里我们采用更通用的方式:用 FS 渲染,再手动用 POI 处理 -->
    <!-- Apache POI -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.3</version>
    </dependency>
</dependencies>

代码示例 (将 HTML 转为带样式的 Word)

这里展示一个更完整的流程:将 HTML 转换为 XWPFDocument,Flying Saucer 的 DocxRenderer 使得这个过程变得非常直接。

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.xhtmlrenderer.docx.DocxRenderer;
import org.xhtmlrenderer.swing.Java2DRenderer;
import java.io.*;
public class HtmlToWordWithFlyingSaucer {
    public static void main(String[] args) {
        String htmlContent = "<html><head><style>" +
                "body { font-family: Arial; }" +
                "h1 { color: blue; text-align: center; }" +
                "p { font-size: 12pt; }" +
                "</style></head>" +
                "<body>" +
                "<h1>Styled Title</h1>" +
                "<p>This paragraph has a style applied.</p>" +
                "</body></html>";
        String outputPath = "output_flying_saucer.docx";
        try {
            convertHtmlToDocx(htmlContent, outputPath);
            System.out.println("Word 文档生成成功: " + outputPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void convertHtmlToDocx(String html, String outputPath) throws Exception {
        // 1. 将 HTML 写入一个临时文件
        File htmlFile = File.createTempFile("temp", ".html");
        try (FileWriter writer = new FileWriter(htmlFile)) {
            writer.write(html);
        }
        // 2. 创建 DocxRenderer 实例
        DocxRenderer docxRenderer = new DocxRenderer();
        // 3. 设置渲染器并渲染 HTML 到 XWPFDocument
        // 第三个参数是宽度,第四个是高度
        try (XWPFDocument document = docxRenderer.renderToDocx(new FileInputStream(htmlFile), 800, 600)) {
            // 4. 保存 Word 文档
            try (FileOutputStream out = new FileOutputStream(outputPath)) {
                document.write(out);
            }
        } finally {
            // 5. 删除临时文件
            htmlFile.delete();
        }
    }
}

注意org.xhtmlrenderer.docx.DocxRenderer 是社区提供的一个实用工具,确保你的 Flying Saucer 版本支持它,如果找不到,你可能需要自己实现一个 Renderer 来将 Flying Saucer 的输出转换为 POI 对象。


使用商业库 (如 Aspose.Words)

对于企业级应用,商业库通常是首选,因为它们功能最全面、最稳定、性能最好。

原理

商业库通常使用高度优化的 C++ 或其他语言内核,并通过 JNI 为 Java 提供接口,它们内部实现了从 HTML 到 OpenXML 格式的直接、高效转换。

优点

  • 功能最全:支持几乎所有的 HTML/CSS 特性,包括复杂的表格、列表、CSS3 布局(Flexbox, Grid)、字体、颜色等。
  • 转换质量最高:生成的 Word 文档与 HTML 在外观上几乎无差别,且内容是可编辑的。
  • 性能卓越:处理大文件和复杂 HTML 时速度飞快。
  • 易于使用:API 设计简单,一行代码即可完成转换。

缺点

  • 收费:需要购买许可证,对于个人或小项目成本较高。
  • 闭源:无法查看源代码,依赖第三方。

Maven 依赖 (需要从官网下载 JAR)

Aspose.Words 的 Maven 仓库通常是收费的,你需要从 Aspose 官网 下载 JAR 文件并手动安装到你的本地仓库或项目中。

<!-- 你需要手动下载并添加这个 JAR -->
<dependency>
    <groupId>com.aspose</groupId>
    <artifactId>aspose-words</artifactId>
    <version>23.8</version> <!-- 使用最新版本 -->
</dependency>

代码示例

import com.aspose.words.*;
public class HtmlToWordWithAspose {
    public static void main(String[] args) throws Exception {
        String htmlContent = "<h1 style='color:red;'>Hello, Aspose.Words!</h1>" +
                "<p>This is a paragraph with <b>bold</b> and <i>italic</i> text.</p>" +
                "<table border='1'><tr><th>Header 1</th><th>Header 2</th></tr><tr><td>Cell 1</td><td>Cell 2</td></tr></table>";
        String outputPath = "output_aspose.docx";
        // 加载 HTML 字符串到 Document 对象
        Document doc = new Document(new ByteArrayInputStream(htmlContent.getBytes("UTF-8")));
        // 保存为 .docx 格式
        doc.save(outputPath);
        System.out.println("Word 文档生成成功: " + outputPath);
    }
}

如你所见,代码极其简洁,转换效果也非常完美。


总结与如何选择

特性 Apache POI + jsoup Flying Saucer + POI Aspose.Words (商业)
易用性 中等 (需要编写较多逻辑) 中等 (配置稍复杂) 极高 (一行代码)
CSS 支持 弱 (基本不支持) (支持大部分 CSS) 极强 (支持 CSS3)
转换质量 一般 (需手动实现) 高 (视觉还原好) 极高 (完美还原)
性能 较好 一般 (渲染引擎开销) 极佳 (底层优化)
成本 免费 免费 收费
适用场景 简单文本转换,对样式要求不高,需要完全免费和可控的场景。 需要保留复杂 CSS 样式的报告生成,如将 HTML 报告转为最终版 Word。 企业级应用,对转换质量、性能、功能有最高要求的项目。

选择建议:

  • 新手或简单需求:从 Apache POI + jsoup 开始,虽然代码多,但能让你理解底层原理。
  • 需要保留复杂样式:如果你的 HTML 有大量 CSS,并且希望 Word 文档看起来一模一样,Flying Saucer 是最佳的开源选择。
  • 企业级生产环境:如果预算允许,Aspose.Words 是最省心、最高效的解决方案,能让你专注于业务逻辑,而不是处理转换的细节。
分享:
扫描分享到社交APP
上一篇
下一篇