在平常工做中,大部分人都會使用 Microsoft Office Word、WPS 或 macOS Pages 等文字處理程序進行 Word 文檔處理。除了使用上述的文字處理程序以外,對於 Word 文檔來講,還有其餘的處理方式麼?答案是有的。javascript
接下來阿寶哥將介紹在前端如何玩轉 Word 文檔,閱讀本文以後,你將瞭解如下內容:html
閱讀阿寶哥近期熱門文章(感謝掘友的鼓勵與支持🌹🌹🌹):前端
小夥伴們準備好了嗎,「玩轉 Word 文檔之旅」 開始了,Let's go!java
Microsoft Office Word 是微軟公司的一個文字處理器應用程序。它最初是由 Richard Brodie 爲了運行 DOS 的 IBM 計算機而在 1983 年編寫的。隨後的版本可運行於 Apple Macintosh(1984 年)、SCO UNIX 和 Microsoft Windows(1989 年),併成爲了 Microsoft Office 的一部分。ios
Word 給用戶提供了用於建立專業而優雅的文檔工具,幫助用戶節省時間,並獲得優雅美觀的結果。一直以來,Microsoft Office Word 都是最流行的文字處理程序。git
下表列出了常見的幾種 Word 支持的文件格式,按擴展名的字母順序排序。github
若想了解 Word 全部支持的格式,可參考微軟 office-file-format-reference 在線文檔。目前你們接觸比較多的是擴展名爲 .docx
的文檔,所以它就是本文的主角。web
俗話說 「知己知彼百戰百勝」,在 「出戰」 前咱們先來簡單瞭解一下 「docx」 文檔。「97-2003 的舊版本文件名後綴就是 .doc, 2007 版之後的後綴名是 .docx」。docx 格式是被壓縮過的文檔,體積更小,能處理更加複雜的內容,訪問速度更快。ajax
實際上 「docx」 文檔是一個壓縮文件( ZIP 格式)。ZIP 文件格式是一種數據壓縮和文檔儲存的文件格式,原名 Deflate,發明者爲菲爾·卡茨(Phil Katz),他於 1989 年 1 月公佈了該格式的資料。ZIP 一般使用後綴名 「.zip」,它的 MIME 格式爲 「application/zip」。npm
這裏阿寶哥已經提早準備了一個包含阿寶哥頭像和某些文本的 「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 文檔進行二次加工,要知足這個需求,咱們就須要先把 Word 文檔轉換成 HTML 文檔。要實現這個功能,有 「服務端轉換和前端轉換」 兩種方案:
在介紹如何利用 Mammoth.js 把以前建立的 Word 文檔轉換成 HTML 文檔前,咱們來提早體驗一下最終的轉換效果。
Mammoth.js 旨在轉換 .docx 文檔(例如由 Microsoft Word 建立的文檔),並將其轉換爲 HTML。 「Mammoth 的目標是經過使用文檔中的語義信息並忽略其餘細節來生成簡單幹淨的 HTML。」 好比,Mammoth 會將應用標題 1 樣式的任何段落轉換爲 h1 元素,而不是嘗試徹底複製標題的樣式(字體,文本大小,顏色等)。
因爲 .docx 使用的結構與 HTML 的結構之間存在很大的不匹配,這意味着對於較複雜的文檔而言,這種轉換不太多是完美的。但若是你僅使用樣式在語義上標記文檔,則 Mammoth 能實現較好的轉換效果。
當前 Mammoth 支持如下主要特性:
它還支持自定義映射規則。例如,你能夠經過提供適當的樣式映射將 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,接下來咱們開始進入實戰環節。
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 這個庫。
JSZip 是一個用於建立、讀取和編輯 「.zip」 文件的 JavaScript 庫,含有可愛而簡單的 API。該庫的兼容性以下所示:
Opera | Firefox | Safari | Chrome | Internet Explorer | Node.js |
---|---|---|---|---|---|
Yes | Yes | Yes | Yes | Yes | Yes |
通過最新版本的測試 | 通過 3.0/3.6/最新版本測試 | 通過最新版本的測試 | 通過最新版本的測試 | 通過 IE 6 / 7 / 8 / 9 / 10 測試 | 通過 Node.js 0.10 / 最新版本測試 |
使用 JSZip 時,你能夠經過如下幾種方式進行安裝:
「npm」: npm install jszip
「bower」:bower install Stuk/jszip
「component」 :component install Stuk/jszip
「手動」:先下載 JSZip 安裝包,而後引入 dist/jszip.js
或 dist/jszip.min.js
文件
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.js saveAs(content, "example.zip"); }); 複製代碼
該示例來自 JSZip 官網,成功運行以後,會自動下載並保存 「example.zip」 文件。該文件解壓後的目錄結構以下所示:
「Markdown 是一種輕量級標記語言」 ,創始人爲約翰·格魯伯(英語:John Gruber)。它容許人們使用易讀易寫的純文本格式編寫文檔,而後轉換成有效的 XHTML(或者 HTML)文檔。這種語言吸取了不少在電子郵件中已有的純文本標記的特性。
因爲 Markdown 的輕量化、易讀易寫特性,而且對於圖片,圖表、數學式都有支持,目前許多網站都普遍使用 Markdown 來撰寫幫助文檔或是用於論壇上發表消息。
瞭解完 Markdown 是什麼以後,咱們來分析一下如何把 Word 文檔轉換成 Markdown 文檔。對於這個功能,咱們也有兩種處理方式:
mammoth.convertToMarkdown(input, options)
方法;
mammoth.convertToHtml(input, options)
生成的 HTML 文檔,在利用 HTML to Markdown 的轉換工具,來間接實現上述功能。
下面咱們來介紹第二種方案,這裏咱們使用 Github 上一個開源的轉換器 —— turndown,它是使用 JavaScript 開發的 HTML to Markdown 轉換器,使用起來很簡單。
首先你能夠經過如下兩種方式來安裝它:
npm install turndown
<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 等全棧乾貨。 ![](https://cdn.xxx.com/rich_159444942843202) 複製代碼
須要注意的是,TurndownService 構造函數支持不少配置項,這裏阿寶哥就不詳細介紹了。感興趣的小夥伴,能夠自行閱讀 turndown 官方文檔或訪問 turndown 在線示例 實際體驗一下。
既然已經講到 Markdown,阿寶哥再給小夥伴們介紹一個 Github 上不錯的開源庫 markmap,該庫使用思惟導圖的方式來實現 Markdown 文檔的可視化,總體效果還蠻不錯的:
(圖片來源:https://markmap.js.org/repl/)
最後,咱們再來看一下在前端如何動態生成 Word 文檔。
在前端若是要動態生成 Word 文檔,咱們能夠直接利用一些成熟的第三方開源庫,好比:docx 或 html-docx-js。
下面咱們將以 docx 爲例,來介紹如何在前端如何生成 「.docx」 格式的 Word 文檔。Docx 這個庫提供了優雅的聲明式 API,讓咱們可使用 JS/TS 輕鬆生成 .docx 文件。此外,它還同時支持 Node.js 和瀏覽器。
Docx 這個庫爲開發者提供了許多類,用於建立 Word 中的對應元素,這裏咱們簡單介紹幾個常見的類:
接下來阿寶哥將使用 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()
方法下載到本地。
本文使用 mdnice 排版