核心思路
无论使用哪种库,其基本原理都是相似的:
- 解析 HTML:将 HTML 字符串或文件解析成一个内存中的文档对象模型。
- 生成 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 是最省心、最高效的解决方案,能让你专注于业务逻辑,而不是处理转换的细节。
