在前端如何玩转 Word 文档
在日常工作中,大部分人都會使用 Microsoft Office Word、WPS 或 macOS Pages 等文字處理程序進行 Word 文檔處理。除了使用上述的文字處理程序之外,對于 Word 文檔來說,還有其他的處理方式么?答案是有的。
接下來阿寶哥將介紹在前端如何玩轉 Word 文檔,閱讀本文之后,你將了解以下內容:
Microsoft Office Word 支持的文件格式和 Docx 文檔的特點;
如何將 Word 文檔轉換成 HTML 文檔;
如何在瀏覽器中處理 ZIP 文檔;
如何將 Word 文檔轉換成 Markdown 文檔;
如何在前端動態生成 Word 文檔。
小伙伴們準備好了嗎,「玩轉 Word 文檔之旅」 開始了,Let's go!
一、Microsoft Office Word 簡介
Microsoft Office Word 是微軟公司的一個文字處理器應用程序。它最初是由 Richard Brodie 為了運行 DOS 的 IBM 計算機而在 1983 年編寫的。隨后的版本可運行于 Apple Macintosh(1984 年)、SCO UNIX 和 Microsoft Windows(1989 年),并成為了 Microsoft Office 的一部分。
Word 給用戶提供了用于創建專業而優雅的文檔工具,幫助用戶節省時間,并得到優雅美觀的結果。一直以來,Microsoft Office Word 都是最流行的文字處理程序。
1.1 Word 支持的文件格式
下表列出了常見的幾種 Word 支持的文件格式,按擴展名的字母順序排序。
若想了解 Word 所有支持的格式,可參考微軟 office-file-format-reference 在線文檔。目前大家接觸比較多的是擴展名為 .docx 的文檔,因此它就是本文的主角。
1.2 Docx 文檔
俗話說 “知己知彼百戰百勝”,在 “出戰” 前我們先來簡單了解一下 「docx」 文檔。「97-2003 的舊版本文件名后綴就是 .doc, 2007 版以后的后綴名是 .docx」。docx 格式是被壓縮過的文檔,體積更小,能處理更加復雜的內容,訪問速度更快。
實際上 「docx」 文檔是一個壓縮文件( ZIP 格式)。ZIP 文件格式是一種數據壓縮和文檔儲存的文件格式,原名 Deflate,發明者為菲爾·卡茨(Phil Katz),他于 1989 年 1 月公布了該格式的資料。ZIP 通常使用后綴名 “.zip”,它的 MIME 格式為 「application/zip」。
這里阿寶哥已經提前準備了一個包含阿寶哥頭像和某些文本的 「abao.docx」 文檔,接著復制一份重命名為 「abao.zip」,然后使用 ZIP 壓縮/解壓軟件進行解壓。
通過觀察解壓后的目錄,我們發現 Word 文檔由一系列的 XML 文件和多媒體文件組成, 「abao.docx」 文檔中的阿寶哥頭像,最終被解壓到 「word/media」 目錄下。下面我們來查看一下 abao 文件夾的目錄結構:
-rw-rw-r--@??1?fer??staff??1641??7?11?01:25?[Content_Types].xml drwxr-xr-x@??3?fer??staff????96??7?11?09:41?_rels drwxr-xr-x@??4?fer??staff???128??7?11?09:41?docProps drwxr-xr-x@?13?fer??staff???416??7?11?09:42?word很明顯 abao 目錄下含有一個 「[Content_Types].xml」 文件和 「_rels、docProps、word」 三個子目錄。
[Content_Types].xml:該文件用于定義里面每一個 XML 文件的內容類型;
_rels:該目錄下一般會有一個 「.rels」 后綴的文件,它里面保存了這個目錄下各個 Part 之間的關系。_rels 目錄不止一個,它實際上是有層級的。
docProps:該目錄下的 XML 文件用于保存 docx 文件的屬性;
word:該目錄下包含了 Word 文檔中的內容、字體、樣式或主題等信息。
介紹完 Word 支持的文件格式和 Docx 文檔,我們開始進入正題 —— 「“在前端如何玩轉 Word 文檔”」。
二、Word 文檔轉換成 HTML 文檔
在日常工作中,有些時候我們希望在富文本編輯器中導入已有的 Word 文檔進行二次加工,要滿足這個需求,我們就需要先把 Word 文檔轉換成 HTML 文檔。要實現這個功能,有 「服務端轉換和前端轉換」 兩種方案:
服務端轉換:對于 Java 開發者來說,可以直接基于 POI 項目,POI 是 Apache 的一個開源項目,它的初衷是處理基于 Office Open XML 標準(OOXML)和 Microsoft OLE 2 復合文檔格式(OLE2)的各種文件格式的文檔,而且支持讀寫操作。
前端轉換:對于前端開發者來說,要想在前端解析 Word 文檔,我們首先需要對 Word 文檔進行解壓,然后再進一步解析解壓后的 XML 文檔??雌饋碚麄€功能實現起來比較繁瑣,但值得慶幸的是 Mammoth.js 這個庫已經為我們實現上述功能。
在介紹如何利用 Mammoth.js 把之前創建的 Word 文檔轉換成 HTML 文檔前,我們來提前體驗一下最終的轉換效果。
2.1 Mammoth.js 簡介
Mammoth.js 旨在轉換 .docx 文檔(例如由 Microsoft Word 創建的文檔),并將其轉換為 HTML。「Mammoth 的目標是通過使用文檔中的語義信息并忽略其他細節來生成簡單干凈的 HTML?!?/strong> 比如,Mammoth 會將應用標題 1 樣式的任何段落轉換為 h1 元素,而不是嘗試完全復制標題的樣式(字體,文本大小,顏色等)。
由于 .docx 使用的結構與 HTML 的結構之間存在很大的不匹配,這意味著對于較復雜的文檔而言,這種轉換不太可能是完美的。但如果你僅使用樣式在語義上標記文檔,則 Mammoth 能實現較好的轉換效果。
當前 Mammoth 支持以下主要特性:
Headings
Lists,Table
Images
Bold, italics, underlines, strikethrough, superscript and subscript
Links,Line breaks
Footnotes and endnotes
它還支持自定義映射規則。例如,你可以通過提供適當的樣式映射將 WarningHeading 轉換為 h1.warning。另外文本框的內容被視為單獨的段落,出現在包含文本框的段落之后。
Mammoth.js 這個庫為我們提供了很多方法,這里我們來介紹三個比較常用的 API:
mammoth.convertToHtml(input, options):把源文檔轉換為 HTML 文檔
mammoth.convertToMarkdown(input, options):把源文檔轉換為 Markdown 文檔。這個方法與 convertToHtml 方法類似,區別就是返回的 result 對象的 value 屬性是 Markdown 而不是 HTML。
mammoth.extractRawText(input):提取文檔的原始文本。這將忽略文檔中的所有格式。每個段落后跟兩個換行符。
介紹完 Mammoth.js 相關的特性和 API,接下來我們開始進入實戰環節。
2.2 Mammoth.js 實戰
Mammoth.js 這個庫同時支持 Node.js 和瀏覽器兩個平臺,在瀏覽器端 mammoth.convertToHtml 方法的 input 參數的格式是 {arrayBuffer: arrayBuffer},其中 arrayBuffer 就是 .docx 文件的內容。在前端我們可以通過 FileReader API ?來讀取文件的內容,此外該接口也提供了 readAsArrayBuffer 方法,用于讀取指定的 Blob 中的內容,一旦讀取完成,result 屬性中保存的將是被讀取文件的 ArrayBuffer 數據對象。下面我們定義一個 readFileInputEventAsArrayBuffer 方法:
export?function?readFileInputEventAsArrayBuffer(event,?callback)?{const?file?=?event.target.files[0];const?reader?=?new?FileReader();reader.onload?=?function(loadEvent:?Event)?{const?arrayBuffer?=?loadEvent.target["result"];callback(arrayBuffer);};reader.readAsArrayBuffer(file); }該方法用于實現把輸入的 File 對象轉換為 ArrayBuffer 對象。在獲取 Word 文檔對應的 ArrayBuffer 對象之后,就可以調用 convertToHtml 方法,把 Word 文檔內容轉換為 HTML 文檔。
mammoth.convertToHtml({?arrayBuffer?})此時如果你的文檔中不包括特殊的圖片類型,比如 wmf 或 emf 類型,而是常見的 jpg 或 png 等類型的話,那么你可以看到 Word 文檔中的圖片。難道這樣就搞定了,那是不是太簡單了,其實這只是個開始。當你通過瀏覽器的開發者工具審查 Word 解析后的 HTML 文檔后,會發現圖片都以 Base64 的格式進行嵌入。如果圖片不多且單張圖片也不會太大的話,那這種方案是可以考慮的。
針對多圖或大圖的情況,一種比較好的方案是把圖片提交到文件資源服務器上。在 Mammoth.js 中要實現上述的功能,可以使用 「convertImage」 配置選項來自定義圖片處理器。具體的使用示例如下:
let?options?=?{convertImage:?mammoth.images.imgElement(function(image)?{return?image.read("base64").then(function(imageBuffer)?{return?{src:?"data:"?+?image.contentType?+?";base64,"?+?imageBuffer};});}) };以上示例實現的功能就是把 Word 中的圖片進行 Base64 編碼,然后轉成 Data URL 的形式,以實現圖片的顯示。很明顯這不符合我們的要求,所以我們需要做以下調整:
const?mammothOptions?=?{convertImage:?mammoth.images.imgElement(function(image)?{return?image.read("base64").then(async?(imageBuffer)?=>?{const?result?=?await?uploadBase64Image(imageBuffer,?image.contentType);return?{src:?result.data.path?//?獲取圖片線上的URL地址};});}) };顧名思義 uploadBase64Image 方法的作用就是上傳 Base64 編碼后的圖片:
async?function?uploadBase64Image(base64Image,?mime)?{const?formData?=?new?FormData();formData.append("file",?base64ToBlob(base64Image,?mime));return?await?axios({method:?"post",url:?"http://localhost:3000/uploadfile",?//?本地圖片上傳的API地址data:?formData,config:?{?headers:?{?"Content-Type":?"multipart/form-data"?}?}}); }為了減少圖片文件的大小,我們需要把 Base64 格式的圖片先轉成 Blob 對象,然后在通過創建 FormData 對象進行提交。base64ToBlob 方法的定義如下:
function?base64ToBlob(base64,?mimeType)?{let?bytes?=?window.atob(base64);let?ab?=?new?ArrayBuffer(bytes.length);let?ia?=?new?Uint8Array(ab);for?(let?i?=?0;?i?<?bytes.length;?i++)?{ia[i]?=?bytes.charCodeAt(i);}return?new?Blob([ia],?{?type:?mimeType?}); }這時把 Word 文檔轉換為 HTML 并自動把 Word 文檔中的圖片上傳至文件資源服務器的基本功能已經實現了。對于 Mammoth.js 內部是如何解析 Word 中的 XML 文件,我們就不做介紹了,反之我們來簡單介紹一下 Mammoth.js 內部依賴的 JSZip 這個庫。
2.3 JSZip 簡介
JSZip 是一個用于創建、讀取和編輯 「.zip」 文件的 JavaScript 庫,含有可愛而簡單的 API。該庫的兼容性如下所示:
| Yes | Yes | Yes | Yes | Yes | Yes |
| 經過最新版本的測試 | 經過 3.0/3.6/最新版本測試 | 經過最新版本的測試 | 經過最新版本的測試 | 經過 IE 6 / 7 / 8 / 9 / 10 測試 | 經過 Node.js 0.10 / 最新版本測試 |
2.3.1 JSZip 安裝
使用 JSZip 時,你可以通過以下幾種方式進行安裝:
「npm」:npm install jszip
「bower」:bower install Stuk/jszip
「component」 :component install Stuk/jszip
「手動」:先下載 ?JSZip 安裝包,然后引入 dist/jszip.js 或 ?dist/jszip.min.js 文件
2.3.2 JSZip 使用示例
let?zip?=?new?JSZip(); zip.file("Hello.txt",?"Hello?Semlinker\n");let?img?=?zip.folder("images"); img.file("smile.gif",?imgData,?{base64:?true}); zip.generateAsync({type:?"blob"}) .then(function(content)?{//?see?FileSaver.jssaveAs(content,?"example.zip"); });該示例來自 JSZip 官網,成功運行之后,會自動下載并保存 「example.zip」 文件。該文件解壓后的目錄結構如下所示:
三、Word 文檔轉換成 Markdown 文檔
「Markdown 是一種輕量級標記語言」 ,創始人為約翰·格魯伯(英語:John Gruber)。它允許人們使用易讀易寫的純文本格式編寫文檔,然后轉換成有效的 XHTML(或者 HTML)文檔。這種語言吸收了很多在電子郵件中已有的純文本標記的特性。
由于 Markdown 的輕量化、易讀易寫特性,并且對于圖片,圖表、數學式都有支持,目前許多網站都廣泛使用 Markdown 來撰寫幫助文檔或是用于論壇上發表消息。
了解完 Markdown 是什么之后,我們來分析一下如何把 Word 文檔轉換成 Markdown 文檔。對于這個功能,我們也有兩種處理方式:
第一種:使用 Mammoth.js 這個庫提供的 mammoth.convertToMarkdown(input, options) 方法;
第二種:基于 mammoth.convertToHtml(input, options) 生成的 HTML 文檔,在利用 HTML to Markdown 的轉換工具,來間接實現上述功能。
下面我們來介紹第二種方案,這里我們使用 Github 上一個開源的轉換器 —— ?turndown,它是使用 JavaScript 開發的 HTML to Markdown 轉換器,使用起來很簡單。
首先你可以通過以下兩種方式來安裝它:
npm:npm install turndown
script:<script src="https://unpkg.com/turndown/dist/turndown.js"></script>
安裝完之后,你就可以通過調用 TurndownService 構造函數,來創建 turndownService 實例,然后調用該實例的 turndown() 方法執行轉換操作:
let?markdown?=?turndownService.turndown(document.getElementById('content') )對于前面使用的 「abao.docx」 文檔,最終轉換生成的 Markdown 文檔如下:
全棧修仙之路,聚焦全棧,專注分享 TypeScript、Web API、Node.js、Deno 等全棧干貨。需要注意的是,TurndownService 構造函數支持很多配置項,這里阿寶哥就不詳細介紹了。感興趣的小伙伴,可以自行閱讀 turndown 官方文檔或訪問 turndown 在線示例 實際體驗一下。
既然已經講到 Markdown,阿寶哥再給小伙伴們介紹一個 Github 上不錯的開源庫 markmap,該庫使用思維導圖的方式來實現 Markdown 文檔的可視化,整體效果還蠻不錯的:
(圖片來源:https://markmap.js.org/repl/)
最后,我們再來看一下在前端如何動態生成 Word 文檔。
四、前端動態生成 Word 文檔
在前端如果要動態生成 Word 文檔,我們可以直接利用一些成熟的第三方開源庫,比如:docx 或 html-docx-js。
下面我們將以 docx 為例,來介紹如何在前端如何生成 「.docx」 格式的 Word 文檔。Docx 這個庫提供了優雅的聲明式 API,讓我們可以使用 JS/TS 輕松生成 .docx 文件。此外,它還同時支持 Node.js 和瀏覽器。
Docx 這個庫為開發者提供了許多類,用于創建 Word 中的對應元素,這里我們簡單介紹幾個常見的類:
Document:用于創建新的 Word 文檔;
Paragraph:用于創建新的段落;
TextRun:用于創建文本,支持設置加粗、斜體和下劃線樣式;
Tables:用于創建表格,支持設置表格每一行和每個表格單元的內容。
接下來阿寶哥將使用 Docx 這個庫,來動態生成前面介紹過的 「abao.docx」 文檔,具體代碼如下所示:
<!DOCTYPE?html> <html><head><meta?charset="UTF-8"?/><meta?name="viewport"?content="width=device-width,?initial-scale=1.0"?/><title></title></head><body><h1>阿寶哥?-?動態生成?Word?文檔示例</h1><button?type="button"?onclick="generate()">點擊生成?Docx?文檔</button><script?src="https://unpkg.com/docx@5.0.2/build/index.js"></script><script?src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js"></script><script>async?function?generate()?{const?doc?=?new?docx.Document();const?imageBuffer?=?await?fetch("https://avatars3.githubusercontent.com/u/4220799").then((response)?=>?response.arrayBuffer());const?image?=?docx.Media.addImage(doc,?imageBuffer,?230,?230);doc.addSection({properties:?{},children:?[new?docx.Paragraph({children:?[new?docx.TextRun({text:?"全棧修仙之路,",bold:?true,}),new?docx.TextRun({text:"聚焦全棧,專注分享 TypeScript、Web API、Node.js、Deno 等全棧干貨。",}),],}),new?docx.Paragraph(image),],});docx.Packer.toBlob(doc).then((blob)?=>?{console.log(blob);saveAs(blob,?"abao.docx");console.log("文檔生成成功");});}</script></body> </html>在以上示例中,當用戶點擊 「點擊生成 Docx 文檔」 按鈕之后,會調用 generate() 回調函數。在該回調函數內,首先會創建新的 Document 對象,然后使用 fetch API 從 Github 上下載阿寶哥的頭像,當成功獲取圖片的數據之后,會繼續調用 docx.Media.addImage() 方法添加圖片。
接著我們會調用 doc.addSection() 方法來添加 Section 塊,該塊將作為段落的容器。在示例中,我們創建的 Section 塊包含兩個段落,一個用于存放文本信息,而另一個用于存放圖片信息。最后我們會把 Document 對象轉換成 Blob 對象,然后通過 saveAs() 方法下載到本地。
五、參考資源
MDN - FileReader
百度百科 - Microsoft Office Word
office-file-format-reference
Github - mammoth.js
關于奇舞周刊
《奇舞周刊》是360公司專業前端團隊「奇舞團」運營的前端技術社區。關注公眾號后,直接發送鏈接到后臺即可給我們投稿。
總結
以上是生活随笔為你收集整理的在前端如何玩转 Word 文档的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【pthread_cancel函数:避免
- 下一篇: 前端静态页面基本开发思路(一)