前陣子聽到公司運營的小姐姐們在抱怨,說在富文本編輯器中發佈包含圖片的 Word 文檔時,圖片和文本內容不能一塊兒複製,每次她們都得分開處理,對於包含較多圖片的 Word 時,她們處理起來很抓狂。目前她們所使用後臺的富文本編輯器是 Ueditor,恰好近期也在研究一款富文本編輯器 —— Editor.js(block styled editor ),也會遇到這種問題,因此就自覺攬下這個小任務。javascript
要解決上述的問題,首先就須要可以解析 Word 文檔中的圖片。目前 Word 有兩種格式後綴分別是 .doc 和 .docx。97-2003 的舊版本文件名後綴就是 .doc, 2007 版之後的後綴名是 .docx。docx 格式是被壓縮過的文檔,體積更小,能處理更加複雜的內容,訪問速度更快。html
對於上述兩種格式的 Word 文檔,你們應該都很熟悉。但估計挺多小夥伴不知道 Word 文檔是如何存儲內容的,這裏咱們以 docx 格式爲例。這裏我已經提早準備了一個包含圖片和文本的 word2html.docx 文件,而後複製一份重命名爲 word2html.zip。看到 zip 後綴相信你已經猜到了,下一步咱們要執行解壓操做。當完成解壓操做以後,默認在當前目錄會生成一個 word2html 文件夾,該文件夾的主要目錄結構以下:前端
感興趣的小夥伴能夠自行解壓一下 Word 文檔,簡單分析一下解壓後的文件。
通過本人認真觀察後發現,在解壓後 Word 文檔中包含的圖片會被保存到 word/media 目錄下。而咱們要解決的問題就是能識別到 Word 文檔中的圖片,而後自動上傳到文件資源服務器。要實現這個功能的前提就是可以解析當前的 Word 文檔,值得慶幸的是這個功能已經有前人幫咱們實現了。java
對於 Java 開發者來講,能夠直接基於 POI 項目,POI 是 Apache 的一個開源項目,它的初衷是處理基於 Office Open XML 標準(OOXML)和 Microsoft OLE 2 複合文檔格式(OLE2)的各類文件格式的文檔,並且支持讀寫操做。固然本文的重點不是服務端解析方案,而是在前端如何實現 Word 解析並提取 Word 中的圖片。一樣對於純前端的解析方案,mwilliamson 大佬已經幫咱們實現了,下面咱們來簡單介紹一下 Mammoth.js 這個庫。ios
Mammoth 旨在轉換 .docx 文檔(例如由 Microsoft Word 建立的文檔),並將其轉換爲 HTML。 Mammoth 的目標是經過使用文檔中的語義信息並忽略其餘細節來生成簡單幹淨的 HTML。好比,Mammoth 會將應用標題 1 樣式的任何段落轉換爲 h1 元素,而不是嘗試徹底複製標題的樣式(字體,文本大小,顏色等)。git
因爲 .docx 使用的結構與 HTML 的結構之間存在很大的不匹配,這意味着對於較複雜的文檔而言,這種轉換不太多是完美的。但若是你僅使用樣式在語義上標記文檔,則 Mammoth 能實現較好的轉換效果。當前 Mammoth 支持如下主要特性:github
它還支持自定義映射規則。例如,你能夠經過提供適當的樣式映射將 WarningHeading 轉換爲 h1.warning。另外文本框的內容被視爲單獨的段落,出如今包含文本框的段落以後。web
Mammoth.js API 爲咱們提供了不少方法,這裏咱們來介紹三個比較經常使用的 API:axios
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 配置選項來自定義圖片處理器。使用示例以下:
var options = { convertImage: mammoth.images.imgElement(function(image) { return image.read("base64").then(function(imageBuffer) { return { src: "data:" + image.contentType + ";base64," + imageBuffer }; }); }) };
上面示例實現的功能就是把圖片轉成 Base64 的格式,很明顯不符合咱們的要求。這裏咱們須要作如下調整:
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 方法的定義以下:
export function base64ToBlob(base64, mime) { mime = mime || ""; const sliceSize = 1024; const byteChars = window.atob(base64); const byteArrays = []; for ( let offset = 0, len = byteChars.length; offset < len; offset += sliceSize ) { const slice = byteChars.slice(offset, offset + sliceSize); const byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } return new Blob(byteArrays, { type: mime }); }
至此解析 Word 文檔並自動把文檔中的圖片上傳至文件資源服務器的基本功能已經實現了。目前該方案遇到的問題就是沒法處理 wmf
和 emf
類型的圖片文件,針對這個問題一開始就想到了七牛雲的圖片處理服務,但閱讀官方相關的使用文檔後,發現全部的圖片處理服務均不支持 wmf
和 emf
類型。固然,期間也嘗試了國外在線的圖片格式化服務和網上一些大佬提供的格式化方案,惋惜的是最終的效果都很差,因此對於這種特殊的圖片格式目前的解決方案就是讓用戶手動上傳對應原始圖片,若是小夥伴們有好的方案,歡迎給我留言喲。