杰瑞科技汇

Java如何将HTML转换成Word?

下面我将为你详细介绍几种主流的实现方法,从简单到强大,并附上代码示例和优缺点分析。

Java如何将HTML转换成Word?-图1
(图片来源网络,侵删)

核心思路

无论使用哪种库,其基本流程都大同小异:

  1. 解析 HTML: 将 HTML 字符串或文件解析成一个内存中的文档对象模型。
  2. 创建 Word 文档: 使用 Java 的 Word 操作库创建一个空的 .docx 文档。
  3. 映射与转换: 将 HTML 的结构(如 <p>, <h1>, <table>, <img>)和样式(如 style="color:red")映射到 Word 的对象(如段落、标题、表格、图片、字体样式)。
  4. : 将转换后的内容填充到 Word 文档中。
  5. 保存文件: 将生成的 Word 文档写入到指定的输出路径。

使用 Apache POI + Jsoup (推荐,功能强大且灵活)

这是最常用和最灵活的组合,Apache POI 是操作 Office 文件的“瑞士军刀”,而 Jsoup 是一个功能强大的 Java HTML 解析器。

  • Apache POI: 负责“创建”和“写入” .docx 文件。
  • Jsoup: 负责“解析” HTML 文件,并提供一个易于操作的 API。

添加 Maven 依赖

<dependencies>
    <!-- Apache POI for .docx creation -->
    <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>

核心转换代码

这个代码示例会处理基本的文本、标题、加粗、斜体、下划线和列表。

import org.apache.poi.xwpf.usermodel.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
public class HtmlToWordConverter {
    public static void main(String[] args) {
        String htmlContent = "<h1>这是一个标题</h1>"
                + "<p>这是一个<b>段落</b>,包含<i>斜体</i>和<u>下划线</u>文本。</p>"
                + "<p>这是一个有序列表:</p>"
                + "<ol>"
                + "  <li>第一项</li>"
                + "  <li>第二项</li>"
                + "</ol>"
                + "<p>这是一个无序列表:</p>"
                + "<ul>"
                + "  <li>项目 A</li>"
                + "  <li>项目 B</li>"
                + "</ul>";
        String outputPath = "output_from_jsoup.docx";
        try {
            convertHtmlToWord(htmlContent, outputPath);
            System.out.println("Word 文档生成成功: " + outputPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void convertHtmlToWord(String html, String outputPath) throws IOException {
        // 1. 使用 Jsoup 解析 HTML
        Document doc = Jsoup.parse(html);
        // 2. 创建 XWPFDocument 对象 (Word文档)
        try (XWPFDocument document = new XWPFDocument();
             // 3. 创建 FileOutputStream 写入文件
             FileOutputStream out = new FileOutputStream(outputPath)) {
            // 4. 递归处理 HTML 的 body 内容
            processNode(doc.body(), document);
            // 5. 保存文档
            document.write(out);
        }
    }
    private static void processNode(Node node, XWPFDocument document) {
        if (node instanceof Element) {
            Element element = (Element) node;
            String tagName = element.tagName().toLowerCase();
            switch (tagName) {
                case "h1":
                case "h2":
                case "h3":
                case "h4":
                case "h5":
                case "h6":
                    // 创建标题,根据 h1-h6 设置不同样式
                    int headingLevel = Integer.parseInt(tagName.substring(1));
                    XWPFParagraph heading = document.createParagraph();
                    heading.setStyle("Heading " + headingLevel); // 使用 Word 内置样式
                    XWPFRun run = heading.createRun();
                    run.setText(element.text());
                    break;
                case "p":
                    // 创建段落
                    XWPFParagraph paragraph = document.createParagraph();
                    run = paragraph.createRun();
                    // 处理段落内的内联样式
                    processInlineStyles(element, run);
                    run.setText(element.text());
                    break;
                case "b":
                case "strong":
                    // 加粗 - 通常在父元素(如p)的run中处理
                    break;
                case "i":
                case "em":
                    // 斜体 - 通常在父元素(如p)的run中处理
                    break;
                case "u":
                    // 下划线 - 通常在父元素(如p)的run中处理
                    break;
                case "ol":
                    // 有序列表 - Jsoup 会自动处理 li,我们只需创建列表
                    createList(document, element, true);
                    break;
                case "ul":
                    // 无序列表
                    createList(document, element, false);
                    break;
                case "img":
                    // 图片处理 (示例,需要完善)
                    // String src = element.attr("src");
                    // System.out.println("发现图片: " + src);
                    break;
                case "table":
                    // 表格处理 (这是一个更复杂的主题,需要单独实现)
                    // createTable(document, element);
                    break;
            }
            // 递归处理子节点
            for (Node child : element.childNodes()) {
                processNode(child, document);
            }
        } else if (node instanceof TextNode) {
            // 处理纯文本节点
            TextNode textNode = (TextNode) node;
            // 文本通常由父元素(如 p)的 run 来处理
        }
    }
    private static void processInlineStyles(Element element, XWPFRun run) {
        String style = element.attr("style");
        if (style == null || style.isEmpty()) {
            return;
        }
        // 简单的样式解析
        if (style.contains("bold") || style.contains("600") || style.contains("700")) {
            run.setBold(true);
        }
        if (style.contains("italic")) {
            run.setItalic(true);
        }
        if (style.contains("underline")) {
            run.setUnderline(UnderlineSingle.SINGLE);
        }
        // 可以添加更多样式,如字体、颜色、大小等
    }
    private static void createList(XWPFDocument document, Element listElement, boolean isOrdered) {
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        if (!paragraphs.isEmpty()) {
            // 获取列表前的最后一个段落,在其后添加列表
            XWPFParagraph lastP = paragraphs.get(paragraphs.size() - 1);
            // POI 的列表功能相对复杂,这里简化处理,实际项目中可能需要更精细的控制
            // 或者使用 XWPFNumbering 来创建真正的 Word 列表
            for (Element li : listElement.children()) {
                XWPFParagraph p = document.createParagraph();
                if (isOrdered) {
                    p.setNumID(getNumberingId(document, true)); // 获取或创建有序列表编号ID
                } else {
                    p.setNumID(getNumberingId(document, false)); // 获取或创建无序列表编号ID
                }
                p.getCTP().getPPr().getNumPr().addNewIlvl().setVal(BigInteger.valueOf(0));
                p.getCTP().getPPr().getNumPr().addNewNumId().setVal(BigInteger.valueOf(p.getNumID()));
                p.createRun().setText(li.text());
            }
        }
    }
    // 辅助方法:获取或创建编号ID
    private static BigInteger getNumberingId(XWPFDocument document, boolean isOrdered) {
        // 这是一个简化的示例,实际实现需要处理 XWPFNumbering 对象
        // 这里返回一个固定的ID,仅用于演示
        // 真实项目中需要根据 isOrdered 来创建或获取对应的 AbstractNum 和 Num
        return BigInteger.valueOf(1); 
    }
}

优缺点

  • 优点:
    • 完全免费和开源
    • 功能极其强大:可以控制 Word 文档的每一个细节,包括页面设置、页眉页脚、复杂表格、图表等。
    • 灵活性高:你可以根据业务需求定制转换逻辑,将特定的 CSS 类映射到特定的 Word 样式。
  • 缺点:
    • 学习曲线较陡:API 相对复杂,尤其是处理复杂布局(如表格、列表、分页)时,代码量会比较大。
    • 样式映射复杂:将 HTML 的 CSS 样式精确地转换为 Word 的样式需要大量的工作。

使用 Flying Saucer (xhtmlrenderer) (适合精确样式还原)

Flying Saucer 是一个基于 CSS 2.1 的 XHTML 渲染器,它的核心思想是:将 HTML/CSS 渲染成一个图片,然后将这个图片插入到 Word 文档中

Java如何将HTML转换成Word?-图2
(图片来源网络,侵删)

这种方法特别适合那些需要精确保留网页布局和样式的场景,比如将一个复杂的 HTML 报告或网页设计稿转换成 Word。

添加 Maven 依赖

<dependencies>
    <!-- Flying Saucer for HTML to PDF/Image rendering -->
    <dependency>
        <groupId>org.xhtmlrenderer</groupId>
        <artifactId>flying-saucer-pdf</artifactId>
        <version>9.1.22</version>
    </dependency>
    <!-- Apache POI to insert the rendered image into Word -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.3</version>
    </dependency>
    <!-- Optional: For better image quality (IText) -->
    <dependency>
        <groupId>org.apache.pdfbox</groupId>
        <artifactId>pdfbox</artifactId>
        <version>2.0.24</version>
    </dependency>
</dependencies>

核心转换逻辑

  1. 准备 HTML: Flying Saucer 需要一个符合 XHTML 规范的文档,我们需要在原始 HTML 的 <head> 部分添加一个 <style> 标签来引入 CSS,并设置正确的 DOCTYPE
  2. 渲染为图片: 使用 Flying Saucer 将 HTML 渲染成一张 BufferedImage
  3. 插入 Word: 使用 Apache POI 创建一个 Word 文档,并将这张图片作为一个 XWPFPicture 插入到文档中。
import org.apache.poi.xwpf.usermodel.*;
import org.xhtmlrenderer.swing.Java2DRenderer;
import org.xhtmlrenderer.util.FSImageWriter;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class HtmlToWordWithFlyingSaucer {
    public static void main(String[] args) {
        // 1. 准备一个符合 XHTML 规范的 HTML 字符串
        String htmlWithCss = "<!DOCTYPE html>"
                + "<html>"
                + "<head>"
                + "<style>"
                + "  body { font-family: Arial, sans-serif; }"
                + "  h1 { color: #2c3e50; }"
                + "  .highlight { background-color: yellow; }"
                + "</style>"
                + "</head>"
                + "<body>"
                + "  <h1>使用 Flying Saucer 转换</h1>"
                + "  <p>这是一个段落,它包含了<span class='highlight'>高亮文本</span>。</p>"
                + "  <table border='1' style='width:50%'>"
                + "    <tr><th>Header 1</th><th>Header 2</th></tr>"
                + "    <tr><td>Row 1 Cell 1</td><td>Row 1 Cell 2</td></tr>"
                + "  </table>"
                + "</body>"
                + "</html>";
        String outputPath = "output_from_flying_saucer.docx";
        try {
            convertHtmlToWordWithImage(htmlWithCss, outputPath);
            System.out.println("Word 文档生成成功: " + outputPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void convertHtmlToWordWithImage(String xhtml, String outputPath) throws IOException {
        // 设置渲染的页面大小
        int width = 800; // 页面宽度 (像素)
        int height = 1024; // 页面高度 (像素)
        // 2. 创建 Java2DRenderer 并渲染 HTML
        Java2DRenderer renderer = new Java2DRenderer(xhtml, width, height);
        BufferedImage image = renderer.getImage();
        // 3. 将 BufferedImage 转换为字节数组 (PNG格式)
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        FSImageWriter imageWriter = new FSImageWriter(); // 默认输出 PNG
        imageWriter.write(image, baos);
        // 4. 使用 Apache POI 创建 Word 文档并插入图片
        try (XWPFDocument document = new XWPFDocument();
             FileOutputStream out = new FileOutputStream(outputPath)) {
            XWPFParagraph paragraph = document.createParagraph();
            XWPFRun run = paragraph.createRun();
            // 从字节数组创建图片
            run.addPicture(baos.toByteArray(), XWPFDocument.PICTURE_TYPE_PNG, "rendered_image.png", Units.toEMU(width), Units.toEMU(height));
            document.write(out);
        }
    }
}

优缺点

  • 优点:
    • 样式还原度极高:能够完美呈现 HTML 和 CSS 的视觉效果,包括布局、颜色、字体等。
    • 实现相对简单:核心逻辑就是“渲染成图”和“插入图片”,不需要处理复杂的 Word 对象模型。
  • 缺点:
    • 内容是图片:转换后的内容不再是可编辑的文本,而是一张图片,用户无法直接复制文本或修改内容。
    • 性能问题:对于非常大的 HTML 文件,渲染过程可能会比较慢,并且消耗较多内存。
    • 图片质量与大小:需要权衡图片的分辨率(DPI)和文件大小。

使用商业组件 (如 Aspose.Words)

如果项目预算允许,商业组件是最高效、最可靠的选择,Aspose.Words 是业界公认的文档处理领域的领导者。

优点

  • 功能最全面:内置了强大的 LoadFormat.LoadHtml 功能,可以直接加载 HTML 并将其转换为 Word 文档对象模型,它对 HTML 和 CSS 的支持非常好,能自动处理大部分转换。
  • 代码最简单:通常只需要几行代码就能完成复杂的转换。
  • 稳定性和性能:经过长期市场验证,非常稳定,性能优异。
  • 技术支持:提供专业的技术支持服务。

缺点

  • 收费:需要购买许可证,对于个人或小型项目可能成本较高。

代码示例 (Aspose.Words)

import com.aspose.words.*;
public class AsposeHtmlToWordConverter {
    public static void main(String[] args) throws Exception {
        String htmlContent = "<h1>Aspose.Words 转换示例</h1><p>这是一个<b>简单</b>的段落。</p>";
        String outputPath = "output_from_aspose.docx";
        // 1. 创建一个 Document 对象
        Document doc = new Document();
        // 2. 使用 DocumentBuilder 加载 HTML
        DocumentBuilder builder = new DocumentBuilder(doc);
        builder.insertHtml(htmlContent);
        // 3. 保存为 .docx 文件
        doc.save(outputPath);
        System.out.println("Word 文档生成成功: " + outputPath);
    }
}

可以看到,代码量非常少,且转换效果非常好。


总结与选择建议

方法 优点 缺点 适用场景
Apache POI + Jsoup 免费、开源、灵活、功能强大 学习曲线陡,样式映射复杂,代码量大 需要精细控制 Word 文档内容,且不希望产生商业成本的项目。
Flying Saucer 样式还原度极高,实现简单 内容不可编辑,性能可能较差 需要将网页设计稿或复杂 HTML 报告原样呈现为 Word 文档的场景。
Aspose.Words 功能最全、代码最简单、最稳定 收费 预算充足,追求开发效率和高可靠性的商业项目。

如何选择?

Java如何将HTML转换成Word?-图3
(图片来源网络,侵删)
  • 如果你的主要需求是转换文本、表格等结构化内容,并且希望内容在 Word 中是可编辑的首选 Apache POI + Jsoup,虽然前期投入多一些,但长期来看最灵活、成本最低。
  • 如果你的核心需求是所见即所得”,必须保留 HTML 的像素级样式选择 Flying Saucer**,接受内容变为图片的事实。
  • 如果你的项目预算充足,并且希望用最少的代码、最快的速度实现一个健壮的转换功能直接选择 Aspose.Words,它能帮你节省大量开发和维护时间。
分享:
扫描分享到社交APP
上一篇
下一篇