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

(图片来源网络,侵删)
核心思路
无论使用哪种库,其基本流程都大同小异:
- 解析 HTML: 将 HTML 字符串或文件解析成一个内存中的文档对象模型。
- 创建 Word 文档: 使用 Java 的 Word 操作库创建一个空的
.docx文档。 - 映射与转换: 将 HTML 的结构(如
<p>,<h1>,<table>,<img>)和样式(如style="color:red")映射到 Word 的对象(如段落、标题、表格、图片、字体样式)。 - : 将转换后的内容填充到 Word 文档中。
- 保存文件: 将生成的 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 文档中。

(图片来源网络,侵删)
这种方法特别适合那些需要精确保留网页布局和样式的场景,比如将一个复杂的 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>
核心转换逻辑
- 准备 HTML: Flying Saucer 需要一个符合 XHTML 规范的文档,我们需要在原始 HTML 的
<head>部分添加一个<style>标签来引入 CSS,并设置正确的DOCTYPE。 - 渲染为图片: 使用 Flying Saucer 将 HTML 渲染成一张
BufferedImage。 - 插入 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 | 功能最全、代码最简单、最稳定 | 收费 | 预算充足,追求开发效率和高可靠性的商业项目。 |
如何选择?

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