關(guān)于Java實(shí)現(xiàn)word(docx、doc)轉(zhuǎn)html的完美解決方案
最近在研究一些關(guān)于文檔轉(zhuǎn)換格式的方法,因?yàn)樾枰迷陂_發(fā)的一個(gè)項(xiàng)目上,所以投入了一些時(shí)間,給大家聊下這塊邏輯及解決方案。
一、關(guān)于word轉(zhuǎn)換html大致都有哪些方法?
(1)使用 Microsoft Word 導(dǎo)出
其實(shí)該方法就是使用word本身導(dǎo)出方案
操作步驟:
- 在 Microsoft Word 中打開文檔。
- 點(diǎn)擊 文件 > 另存為 或 導(dǎo)出。
- 選擇保存類型為 網(wǎng)頁(.html, .htm)。
- 保存文件后,會(huì)生成一個(gè) HTML 文件(有時(shí)會(huì)附帶一個(gè)文件夾用于存放圖片等資源)。
優(yōu)點(diǎn):
- 保留了文檔的大部分格式。
- 操作簡單,無需其他工具。
缺點(diǎn):
- 導(dǎo)出的 HTML 文件代碼較冗余,包含許多與 Word 相關(guān)的樣式和標(biāo)簽。
(2)使用第三方工具或在線轉(zhuǎn)換工具
一般常見的有SmallPDF、Zamzar、Convertio、LibreOffice等在線工具或軟件進(jìn)行轉(zhuǎn)換
優(yōu)點(diǎn):
- 方便快捷,適合大多數(shù)人使用。
- 有些工具可以清理冗余代碼,生成更簡潔的 HTML。
缺點(diǎn):
- 在線工具可能存在隱私和安全風(fēng)險(xiǎn)。
- 某些工具可能無法完全保留復(fù)雜文檔的格式。
(3)使用編程實(shí)現(xiàn)自動(dòng)化轉(zhuǎn)換
常見的編程實(shí)現(xiàn)有:
- Python:
- 使用
python-docx庫讀取 .docx 文件,再用自定義邏輯生成 HTML。 - 使用
mammoth庫,專門將 .docx 轉(zhuǎn)為干凈的 HTML(推薦)。 - 使用
pywin32調(diào)用 Windows COM 接口操作 Microsoft Word。
- 使用
- Java:
- 使用 Apache POI 的
XWPF模塊解析 .docx 文件并輸出 HTML。
- 使用 Apache POI 的
- Node.js:
- 使用
officegen或mammoth.js轉(zhuǎn)換 .docx 文件。
- 使用
- C#:
- 使用 OpenXML SDK 或 Interop.Word 來操作 Word 文件并轉(zhuǎn)換為 HTML。
本此講解的就是通過java的poi內(nèi)的模塊進(jìn)行解析輸出html
二、docx轉(zhuǎn)換html
示例代碼如下:
public static void docxtoHtml(String fileName, String outPutFile) throws TransformerException, IOException, ParserConfigurationException {
long startTime = System.currentTimeMillis();
XWPFDocument document = new XWPFDocument(new FileInputStream(fileName));
// 用于存儲(chǔ)目錄內(nèi)容
StringBuilder toc = new StringBuilder();
toc.append("<div id='toc'>\n<ul>\n"); // 直接從 <ul> 開始,表示目錄
// 遍歷文檔中的段落,查找目錄項(xiàng)
List<XWPFParagraph> paragraphs = document.getParagraphs();
int tocLevel = 0; // 目錄的當(dāng)前級(jí)別,1代表一級(jí)目錄,2代表二級(jí)目錄,3代表三級(jí)目錄
boolean tocStarted = false; // 標(biāo)記目錄是否開始
for (XWPFParagraph paragraph : paragraphs) {
String style = paragraph.getStyle(); // 獲取段落樣式
String text = paragraph.getText(); // 獲取段落文本
// 根據(jù)段落的樣式級(jí)別來識(shí)別目錄項(xiàng),假設(shè)標(biāo)題樣式為 Heading 1, Heading 2, Heading 3
if (style != null) {
if (style.equals("Heading 1")) { // 一級(jí)標(biāo)題
if (tocStarted) {
toc.append("</ul>\n"); // 關(guān)閉上一級(jí)目錄
}
toc.append("<ul>\n"); // 開始一個(gè)新的無序列表
toc.append("<li><a href='#" + text.hashCode() + "'>" + text + "</a></li>\n");
tocLevel = 1;
tocStarted = true;
} else if (style.equals("Heading 2")) { // 二級(jí)標(biāo)題
if (tocLevel == 1) {
toc.append("<ul>\n"); // 開始二級(jí)目錄
}
toc.append("<li><a href='#" + text.hashCode() + "'>" + text + "</a></li>\n");
tocLevel = 2;
} else if (style.equals("Heading 3")) { // 三級(jí)標(biāo)題
if (tocLevel == 2) {
toc.append("<ul>\n"); // 開始三級(jí)目錄
}
toc.append("<li><a href='#" + text.hashCode() + "'>" + text + "</a></li>\n");
tocLevel = 3;
}
}
// 在目錄項(xiàng)前插入錨點(diǎn)
if (style != null && (style.equals("Heading 1") || style.equals("Heading 2") || style.equals("Heading 3"))) {
String anchor = "<a name='" + text.hashCode() + "'></a>";
String modifiedText = anchor + text; // 在目錄項(xiàng)文本前添加錨點(diǎn)
// 更新段落中的文本
for (XWPFRun run : paragraph.getRuns()) {
run.setText(modifiedText, 0); // 更新段落內(nèi)容
}
}
}
// 如果目錄結(jié)束后,確保關(guān)閉所有的<ul>標(biāo)簽
if (tocLevel > 0) {
toc.append("</ul>\n");
}
toc.append("</ul>\n</div>\n"); // 關(guān)閉最外層的 <ul> 和 <div>
// 設(shè)置XHTMLOptions
XHTMLOptions options = XHTMLOptions.create().indent(4);
File imageFolder = new File(tempPath); // 圖片臨時(shí)文件夾路徑
options.setExtractor(new FileImageExtractor(imageFolder));
options.URIResolver(new FileURIResolver(imageFolder));
// 使用 `XHTMLConverter` 進(jìn)行 DOCX 到 HTML 的轉(zhuǎn)換
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
XHTMLConverter.getInstance().convert(document, byteArrayOutputStream, options);
System.out.println("Generate " + outPutFile + " with " + (System.currentTimeMillis() - startTime) + " ms.");
// 獲取轉(zhuǎn)換后的HTML內(nèi)容
String htmlContent = new String(byteArrayOutputStream.toByteArray(), "UTF-8");
// 將TOC插入到HTML的開頭
htmlContent = toc + htmlContent;
// 處理分頁符:將分頁符添加到HTML中
htmlContent = htmlContent.replaceAll("<!-- PAGE_BREAK -->", "<div class='page-break'></div>");
// 添加表格樣式(邊框)
htmlContent = htmlContent.replaceAll("<table>", "<table style='border: 1px solid black !important; border-collapse: collapse; width: 100%;'>");
htmlContent = htmlContent.replaceAll("<td>", "<td style='border: 1px solid black !important; padding: 5px; text-align: left;'>");
htmlContent = htmlContent.replaceAll("<th>", "<th style='border: 1px solid black !important; padding: 5px; text-align: left;'>");
htmlContent = htmlContent.replaceAll("<tr>", "<tr style='border: 1px solid black !important;'>");
htmlContent = htmlContent.replaceAll("<thead>", "<thead style='border: 1px solid black !important;'>");
htmlContent = htmlContent.replaceAll("<tbody>", "<tbody style='border: 1px solid black !important;'>");
htmlContent = htmlContent.replaceAll("<tfoot>", "<tfoot style='border: 1px solid black !important;'>");
// 增加全局CSS樣式(確保表格和目錄樣式正確)
String style = "<style>\n" +
"table { border: 1px solid black !important; border-collapse: collapse; width: 100%; }\n" +
"td, th { border: 1px solid black !important; padding: 5px; text-align: left; }\n" +
"tr { border: 1px solid black !important; }\n" +
"ul { list-style-type: none; padding: 0; }\n" + // 去掉默認(rèn)的列表樣式
"li { margin: 5px 0; }\n" + // 設(shè)置目錄項(xiàng)的間距
"</style>\n";
htmlContent = style + htmlContent;
// 將最終的HTML內(nèi)容寫入文件
writeFile(htmlContent, outPutFile);
}該方法功能實(shí)現(xiàn):
- 將
.docx文件轉(zhuǎn)換為 HTML 文件。 - 自動(dòng)生成基于文檔標(biāo)題的目錄 (TOC)。
- 為標(biāo)題添加錨點(diǎn)鏈接,支持 HTML 頁面內(nèi)跳轉(zhuǎn)。
- 處理分頁符,將其轉(zhuǎn)換為 HTML 的
<div>元素。 - 增強(qiáng)表格樣式,添加邊框和對(duì)齊(有時(shí)原表格css樣式轉(zhuǎn)換后會(huì)被覆蓋掉)。
- 為 HTML 頁面添加全局 CSS 樣式,保證視覺效果統(tǒng)一。
三、doc轉(zhuǎn)換html
示例代碼如下:
public static void doctoHtml(String fileName, String outPutFile) throws TransformerException, IOException, ParserConfigurationException {
// 開始計(jì)時(shí)
long startTime = System.currentTimeMillis();
// 讀取 Word 文檔
HWPFDocument wordDocument = new HWPFDocument(new FileInputStream(fileName));
// 創(chuàng)建 Word 轉(zhuǎn) HTML 轉(zhuǎn)換器
WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());
// 圖片保存路徑設(shè)置
String imageFolderPath = tempPath + "images" + File.separator; // 存儲(chǔ)圖片的絕對(duì)路徑
// 設(shè)置圖片管理器,處理圖片保存邏輯
wordToHtmlConverter.setPicturesManager(new PicturesManager() {
public String savePicture(byte[] content, PictureType pictureType, String suggestedName, float widthInches, float heightInches) {
String picturePath = imageFolderPath + suggestedName; // 圖片保存路徑
File imageFolder = new File(imageFolderPath);
if (!imageFolder.exists()) {
boolean created = imageFolder.mkdirs(); // 創(chuàng)建圖片文件夾
if (created) {
System.out.println("在以下位置創(chuàng)建圖片文件夾:" + imageFolder.getAbsolutePath());
} else {
System.out.println("創(chuàng)建圖片文件夾失敗");
}
}
try {
File pictureFile = new File(picturePath);
try (FileOutputStream fos = new FileOutputStream(pictureFile)) {
fos.write(content); // 寫入圖片數(shù)據(jù)
System.out.println("圖片保存路徑" + pictureFile.getAbsolutePath());
}
} catch (IOException e) {
e.printStackTrace();
}
return picturePath; // 返回圖片路徑
}
});
// 處理文檔內(nèi)容,轉(zhuǎn)換為 HTML
wordToHtmlConverter.processDocument(wordDocument);
// 獲取生成的 HTML 文檔
Document htmlDocument = wordToHtmlConverter.getDocument();
// 自定義分頁符處理:查找文檔中的分頁符并插入到 HTML 中
NodeList bodyNodes = htmlDocument.getElementsByTagName("body");
if (bodyNodes.getLength() > 0) {
Node bodyNode = bodyNodes.item(0); // 獲取 HTML 中的 <body> 節(jié)點(diǎn)
NodeList paragraphs = bodyNode.getChildNodes();
for (int i = 0; i < paragraphs.getLength(); i++) {
Node paragraph = paragraphs.item(i);
if (paragraph.getNodeType() == Node.ELEMENT_NODE && paragraph.getNodeName().equals("p")) {
String innerText = paragraph.getTextContent();
if (innerText.contains("\f")) { // 檢查是否包含分頁符(\f)
// 創(chuàng)建自定義分頁符 HTML 元素
Element pageBreak = htmlDocument.createElement("div");
pageBreak.setAttribute("class", "page-break"); // 設(shè)置 class 屬性,方便樣式控制
pageBreak.appendChild(htmlDocument.createTextNode(" "));
// 在分頁符前插入自定義分頁符標(biāo)記
bodyNode.insertBefore(pageBreak, paragraph);
}
}
}
}
// 將 HTML 文檔輸出為字節(jié)流
ByteArrayOutputStream out = new ByteArrayOutputStream();
DOMSource domSource = new DOMSource(htmlDocument);
StreamResult streamResult = new StreamResult(out);
// 使用 Transformer 進(jìn)行 HTML 格式化輸出
TransformerFactory tf = TransformerFactory.newInstance();
Transformer serializer = tf.newTransformer();
serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8"); // 設(shè)置編碼為 UTF-8
serializer.setOutputProperty(OutputKeys.INDENT, "yes"); // 格式化輸出
serializer.setOutputProperty(OutputKeys.METHOD, "html"); // 輸出格式為 HTML
serializer.transform(domSource, streamResult);
out.close();
// 將字節(jié)流轉(zhuǎn)換為字符串
String htmlContent = new String(out.toByteArray());
// 處理特殊標(biāo)記符,例如去掉目錄標(biāo)記(根據(jù)需要調(diào)整)
htmlContent = htmlContent.replaceAll("TOC \\\\o \"1-3\" \\\\h \\\\z \\\\u", "");
// 將生成的 HTML 內(nèi)容寫入文件
writeFile(htmlContent, outPutFile);
// 輸出生成文件的信息及用時(shí)
System.out.println("Generate " + outPutFile + " with " + (System.currentTimeMillis() - startTime) + " ms.");
}該方法功能實(shí)現(xiàn):
- 將
.doc格式的 Word 文檔轉(zhuǎn)換為 HTML 文件。 - 提取并保存文檔中的圖片到指定路徑,并在 HTML 中插入圖片引用。
- 處理分頁符,將分頁符(
\f)替換為自定義的 HTML 標(biāo)記。 - 格式化生成的 HTML 文件,便于閱讀和使用。
到此這篇關(guān)于關(guān)于java實(shí)現(xiàn)word(docx、doc)轉(zhuǎn)html的解決方案的文章就介紹到這了,更多相關(guān)java word轉(zhuǎn)html內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 使用Java和Apache POI實(shí)現(xiàn)HTML轉(zhuǎn)Word的完整指南
- Java實(shí)現(xiàn)將Word轉(zhuǎn)換成Html的示例代碼
- Java實(shí)現(xiàn)word/pdf轉(zhuǎn)html并在線預(yù)覽
- Java實(shí)現(xiàn)HTML轉(zhuǎn)為Word的示例代碼
- Java 將Word轉(zhuǎn)為HTML的方法
- java使用POI實(shí)現(xiàn)html和word相互轉(zhuǎn)換
- java實(shí)現(xiàn)word文件轉(zhuǎn)html文件
- Java實(shí)現(xiàn)將HTML文件與字符串轉(zhuǎn)為Word
相關(guān)文章
SpringBoot使用Swagger生成多模塊的API文檔
這篇文章將以?Spring?Boot?多模塊項(xiàng)目為例,為大家詳細(xì)介紹一下如何使用?Swagger?生成多模塊的?API?文檔,感興趣的小伙伴可以了解一下2025-02-02
Java excel數(shù)據(jù)導(dǎo)入mysql的實(shí)現(xiàn)示例詳解
今天教大家如何使用Java將excel數(shù)據(jù)導(dǎo)入MySQL,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴呢很有幫助,需要的朋友可以參考下2022-08-08
SpringBoot項(xiàng)目中忽略某屬性返回?cái)?shù)據(jù)給前端
在Spring Boot中,保護(hù)敏感信息和減少數(shù)據(jù)傳輸是很重要的,我們可以使用多種方法來忽略返回?cái)?shù)據(jù)中的字段,無論是使用@JsonIgnore注解、Projection投影、@JsonIgnoreProperties注解還是自定義序列化器,都能達(dá)到我們的目的,在實(shí)際應(yīng)用中,根據(jù)具體場景和需求選擇合適的方法2024-05-05
Java動(dòng)態(tài)規(guī)劃方式解決不同的二叉搜索樹
二叉搜索樹作為一個(gè)經(jīng)典的數(shù)據(jù)結(jié)構(gòu),具有鏈表的快速插入與刪除的特點(diǎn),同時(shí)查詢效率也很優(yōu)秀,所以應(yīng)用十分廣泛。本文將詳細(xì)講講二叉搜索樹的原理與實(shí)現(xiàn),需要的可以參考一下2022-10-10
MyBatis-Plus主鍵生成策略的實(shí)現(xiàn)方法
MyBatis-Plus提供了多種主鍵生成策略,包括自增、UUID、Redis和雪花算法,本文就來介紹一下,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
java使用CompletableFuture分批處理任務(wù)實(shí)現(xiàn)
本文主要介紹了java使用CompletableFuture分批處理任務(wù)實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07

