<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      松鼠的博客

      導航

      富文本編輯器復制word文檔中的圖片

      文章有點長,感覺每次寫文章都特別啰嗦,如果不想看過程的話直接跳到*動手實踐那一步,那邊有核心的方法~

      富文本編輯器復制 word 文檔中的圖片
      問題點:從 word 文檔復制進來的內容的圖片都是 file:/// 協議,這時候如果我們的頁面是 http://或者 https:// 協議的話,就不允許讀取圖片了。

       


      除非頁面也是本地文件打開的(但是實際項目中基本上是不可能的了):

       

       

      與 ckeditor 相見恨晚
      paste-from-word demo

       

       

      看,ckeditor 就支持!然而這時候的項目已經有太多歷史包袱(包括后面新開發的插件,我用的是 tinymce )

      倒不是說 tinymce 不好,只是用多了你會發現。。。真的很不好(說來話長,后面記錄 tinymce 的時候在吐槽把)

      如果你也有編輯器需求,而且沒有歷史包袱,直接嘗試 ckeditor 把

      獲取圖片的前奏
      要獲取圖片,先從剪貼板入手,因為我們的數據源最后是從剪貼板復制過來的。

      先了解幾個知識點,才能更好理解后面的內容

      為什么網站不能直接讀取圖片?因為安全性:
      ckeditor 在怎么強大也不可能從 http/https 協議下的網址讀取 file:/// 的文件。原因也很簡單,如果能讀取的話,豈不是網站能把我們全部的資料都讀到?

      word 文檔內部的東西
      word 文檔其實只需要把后綴改為 .zip。然后打開對應的目錄,你會發現圖片就存在里面,而且 word 目錄下還有一個 webSettings.xml 里面就存放著 word 文檔的信息。感興趣的就自己找一個看看把

       

       

      關于系統剪貼板/JS 中的 clipboardData
      我們經常用到的復制某一段字的功能,其實核心就是用到了 window 子對象 clipboardData 的一個方法:setData()

      clipboardData.setData(sDataFormat, sData)

      sDataFormat:要復制的內容的格式;
      sData:要復制的內容。
      只是因為 clipboardData 還是實驗性功能,所以平時用的不多。接下來要說的東西就和 sDataFormat 息息相關。

      獲取剪貼板內容
      主動獲取
      缺點:

      只能在 https 域名下使用(見下圖 1)
      頁面必須聚焦,鼠標在控制臺都不行(見下圖 2)
      還會被人發現,甚至被人拒絕(見下圖 3)
      優點:

      他能讓你獲取剪貼板內容。。。

       

       

       

       

       

      navigator.clipboard
      .readText()
      .then(v => {
      console.log('獲取剪貼板成功:', v)
      })
      .catch(v => {
      console.log('獲取剪貼板失敗: ', v)
      })

      被控獲取 監聽 ctrl + v / 粘貼事件
      使用 event 中的 clipboardData 調用 getData 方法,其中的參數目前我知道的有如下幾個

      text 獲取文本
      text/html 獲取 html 文本
      text/plain 獲取普通文本,效果和 text 一樣
      text/rtf 獲取 rtf 信息 (不懂就問,啥是 rtf)
      window.addEventListener('paste', function(e) {
      const clipdata = e.clipboardData || window.clipboardData
      let data = clipdata.getData('text/html')
      console.log(data)
      })

      PS:復制后到頁面上隨便粘貼一下,不一定要找到輸入框,按下 ctrl+v 就行

      輸出如下:上面還有一大堆亂七八糟的標簽,wps 就比 office 干凈多了,這個是從 office 復制進來的。

       

       

      clipdata.getData('text/html') 也就是我們富文本用的方法,獲取粘貼的內容的 html 代碼 注意是 text/html 這里有個坑,后面會說到
      clipdata.getData('text/rtf') 獲取的東西更加亂了,不過里面就記載著我們的圖片信息(我的文檔就 2 張圖片,11mb.可怕)

       


      有了上面的基礎知識,我們就能拋開富文本編輯器,先來實現一個文章最前面的截圖,粘貼顯示 word 文檔的功能。

      <body>
      <p>請按下ctrl+v粘貼內容</p>
      <div id="preview"></div>
      <script>
      window.addEventListener("paste", function (e) {
      const clipdata = e.clipboardData || window.clipboardData;
      document.querySelector('#preview').innerHTML = clipdata.getData("text/html")
      });
      </script>
      </body>
      </html>

      獲取 word 文檔中的圖片
      下面根據 ckeditor 的源碼來學習,具體的代碼是在

      GitHub:ckeditor5-paste-from-office

      或者從 npm 下載:@ckeditor/ckeditor5-paste-from-office

      分析源碼:

      src/index.js -> src/pastefromoffice.js (在 init 函數中,執行了一個 activeNormalizer.execute方法)-> src/normalizers/mswordnormalizer.js

      到這里就看到了一個 replaceImagesSourceWithBase64 方法,這就是今天學習的核心

      replaceImagesSourceWithBase64 方法
      該方法在:src/filters/image.js

      在 replaceImagesSourceWithBase64 函數中,和圖片相關的方法是:

      findAllImageElementsWithLocalSource 查找全部的 file:/// 開頭的圖片
      createRangeIn、new Matcher、這些方法都不用太過于關注,因為復制進來的都是文本,這些可能是 ckeditor 核心代碼中轉換為 dom 節點的方法
      我們直接粗暴點渲染為真實 dom,然后在操作真實 dom 就是了
      第 12 行,獲取 src 是 file:// 開頭的 dom 節點

      function findAllImageElementsWithLocalSource(documentFragment, writer) {
      const range = writer.createRangeIn(documentFragment)

      const imageElementsMatcher = new Matcher({
      name: 'img'
      })

      const imgs = []

      for (const value of range) {
      if (imageElementsMatcher.match(value.item)) {
      if (value.item.getAttribute('src').startsWith('file://')) {
      imgs.push(value.item)
      }
      }
      }

      return imgs
      }


      接著執行 replaceImagesFileSourceWithInlineRepresentation 方法。在這之前還會執行 extractImageDataFromRtf
      extractImageDataFromRtf 方法
      同樣是在 src/filters/image.js

      這部分代碼是把我們從剪貼板中 getData('text/rtf') 獲取到的值做一個加工,提取里面的圖片信息(我承認沒看懂提取的是啥,我對 rtf 也不那么了解,哈哈哈哈)

      更新一點點東西(關于正則無法匹配到最新的圖片節點)
      regexPictureHeader 這段正則中,在以前的時候還是可以用的,可能最近 rtf 又更新了,導致匹配失敗,無法生成圖片
      于是進過一番探索,根據舊的正則自己刪減了一部分匹配規則,進過測試 office 和 wps 都能識別。
      舊的寫法: const regexPictureHeader = /{\pict[\s\S]+?\bliptag-?\d+(\blipupi-?\d+)?({\*\blipuid\s?[\da-fA-F]+)?[\s}]?/;
      新的寫法:const regexPictureHeader = /{\pict[\s\S]+?({\*\blipuid\s?[\da-fA-F]+)[\s}]/;

      function extractImageDataFromRtf(rtfData) {
      if (!rtfData) {
      return []
      }

      // 舊的寫法
      // const regexPictureHeader = /{\\pict[\s\S]+?\\bliptag-?\d+(\\blipupi-?\d+)?({\\\*\\blipuid\s?[\da-fA-F]+)?[\s}]*?/
      // 新刪減后的寫法
      const regexPictureHeader = /{\\pict[\s\S]+?({\\\*\\blipuid\s?[\da-fA-F]+)[\s}]*/
      const regexPicture = new RegExp('(?:(' + regexPictureHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g')
      const images = rtfData.match(regexPicture)
      const result = []

      if (images) {
      for (const image of images) {
      let imageType = false

      if (image.includes('\\pngblip')) {
      imageType = 'image/png'
      } else if (image.includes('\\jpegblip')) {
      imageType = 'image/jpeg'
      }

      if (imageType) {
      result.push({
      hex: image.replace(regexPictureHeader, '').replace(/[^\da-fA-F]/g, ''),
      type: imageType
      })
      }
      }
      }

      return result
      }


      replaceImagesFileSourceWithInlineRepresentation
      同文件下的方法

      傳入的參數第一個是 src 為file://的圖片節點數組,第二個從 rtf 提取的圖片信息數組,第三個就是 ckeditor 自己的方法了,用來顯示文本的,不用管他

      還用到了一個 _convertHexToBase64 方法,把 hex 轉換為 base64

      接著就是一頓循環了,對應的節點替換為對應的 base64,設置到圖片節點的的 src 上,只是這里他們用了自身封裝的 writer。

      function replaceImagesFileSourceWithInlineRepresentation(imageElements, imagesHexSources, writer) {
      // Assume there is an equal amount of image elements and images HEX sources so they can be matched accordingly based on existing order.
      if (imageElements.length === imagesHexSources.length) {
      for (let i = 0; i < imageElements.length; i++) {
      const newSrc = `data:${imagesHexSources[i].type};base64,${_convertHexToBase64(imagesHexSources[i].hex)}`
      writer.setAttribute('src', newSrc, imageElements[i])
      }
      }
      }

      function _convertHexToBase64(hexString) {
      return btoa(
      hexString
      .match(/\w{2}/g)
      .map(char => {
      return String.fromCharCode(parseInt(char, 16))
      })
      .join('')
      )
      }


      動手實踐,獲取圖片信息并展示
      上面分析了一些 ckeditor 代碼之后,其實我們要用的也就是

      findAllImageElementsWithLocalSource
      這個方法被改造了一下,直接讀取實際的 dom 節點,拿到圖片節點
      replaceImagesFileSourceWithInlineRepresentation
      這個方法在最后賦值的時候也改了下,因為我們已經記錄了實際的 dom 節點,所以直接使用 .setAttribute(‘src’,newSrc)
      extractImageDataFromRtf
      _convertHexToBase64
      整理過后的代碼如下:

      <body>
      <p>請按下ctrl+v粘貼內容</p>
      <div id="preview"></div>

      <script>
      window.addEventListener("paste", function (e) {
      const clipdata = e.clipboardData || window.clipboardData;
      document.querySelector('#preview').innerHTML = clipdata.getData("text/html")
      let rtf = clipdata.getData('text/rtf')

      let imgs = findAllImageElementsWithLocalSource()

      replaceImagesFileSourceWithInlineRepresentation(imgs, extractImageDataFromRtf(rtf))

      });

      function findAllImageElementsWithLocalSource() {
      let imgs = document.querySelectorAll('img')
      return imgs;
      }

      function extractImageDataFromRtf(rtfData) {
      if (!rtfData) {
      return [];
      }

      // 舊的寫法
      // const regexPictureHeader = /{\\pict[\s\S]+?\\bliptag-?\d+(\\blipupi-?\d+)?({\\\*\\blipuid\s?[\da-fA-F]+)?[\s}]*?/
      // 新刪減后的寫法
      const regexPictureHeader = /{\\pict[\s\S]+?({\\\*\\blipuid\s?[\da-fA-F]+)[\s}]*/
      const regexPicture = new RegExp('(?:(' + regexPictureHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g');
      const images = rtfData.match(regexPicture);
      const result = [];

      if (images) {
      for (const image of images) {
      let imageType = false;

      if (image.includes('\\pngblip')) {
      imageType = 'image/png';
      } else if (image.includes('\\jpegblip')) {
      imageType = 'image/jpeg';
      }

      if (imageType) {
      result.push({
      hex: image.replace(regexPictureHeader, '').replace(/[^\da-fA-F]/g, ''),
      type: imageType
      });
      }
      }
      }

      return result;
      }

      function _convertHexToBase64(hexString) {
      return btoa(hexString.match(/\w{2}/g).map(char => {
      return String.fromCharCode(parseInt(char, 16));
      }).join(''));
      }

      function replaceImagesFileSourceWithInlineRepresentation(imageElements, imagesHexSources, writer) {
      // Assume there is an equal amount of image elements and images HEX sources so they can be matched accordingly based on existing order.
      if (imageElements.length === imagesHexSources.length) {
      for (let i = 0; i < imageElements.length; i++) {
      const newSrc = `data:${imagesHexSources[i].type};base64,${_convertHexToBase64(imagesHexSources[i].hex)}`;

      imageElements[i].setAttribute('src',newSrc)
      }
      }
      }

      </script>

      </body>

      </html>


      錦上添花,實現圖片上傳
      進過上面一系列方法后,我們確實是拿到了 base64 格式的圖片,可是這顯示未免也太長了一些,如果要實現上傳,還得后端給我們重新起一個 base64 圖片上傳的方法。。。

      base64 轉換為 blod 對象
      blod 就是我們平時用 input 選擇圖片后拿到的 File 類型(不知道有沒有解釋錯,大概就是這個意思)

      方法如下:

      /** 將base64轉換為文件對象
      * @param {String} base64 base64字符串
      *
      */
      function convertBase64ToBlob(base64) {
      var base64Arr = base64.split(',')
      var imgtype = ''
      var base64String = ''
      if (base64Arr.length > 1) {
      //如果是圖片base64,去掉頭信息
      base64String = base64Arr[1]
      imgtype = base64Arr[0].substring(base64Arr[0].indexOf(':') + 1, base64Arr[0].indexOf(';'))
      }
      // 將base64解碼
      var bytes = atob(base64String)
      //var bytes = base64;
      var bytesCode = new ArrayBuffer(bytes.length)
      // 轉換為類型化數組
      var byteArray = new Uint8Array(bytesCode)

      // 將base64轉換為ascii碼
      for (var i = 0; i < bytes.length; i++) {
      byteArray[i] = bytes.charCodeAt(i)
      }

      // 生成Blob對象(文件對象)
      return new Blob([bytesCode], { type: imgtype })
      }


      效果如下

       

       

      優化顯示的 URL
      上傳問題是解決了,可是那么長的 base64 看著實在是糟心,還好我們還有 ObjectURL

      一下子清爽多了:

       


      let boldFile = convertBase64ToBlob('base64的字符串')
      // 直接使用 URL.createObjectURL 生成
      imageElements[i].setAttribute('src', URL.createObjectURL(boldFile))

      blod 轉 base64
      既然都說到這里了,還有一個轉換就順便說了把

      function readBlobAsDataURL(blob, callback) {
      var a = new FileReader()
      a.onload = function(e) {
      callback(e.target.result)
      }
      a.readAsDataURL(blob)
      }

      readBlobAsDataURL('blod文件對象', function(base64) {
      console.log(base64)
      })

      圖片讀取,圖片顯示,包括圖片轉換為 blod 對象也有了,只要圖片上傳后,在回顯一下,就齊活了~

      總結
      核心原理包括 ckeditor 部分源碼解讀就結束了,當然還有很多細節沒考慮,包括一些標簽的轉換,標簽過濾,樣式過濾,最主要的是要判斷復制進來的到底是不是 word 文檔,還有如果拿不到 rtf 等各種情況,都可以研究下 ckeditor 的代碼

      流程總結
      監聽粘貼事件,獲取剪貼板的數據(包括 text/html和text/rtf)
      拿到 html 后把 file:// 開頭的 img 節點找出來,然后使用轉換方法把 rtf 對應的圖片信息也一一對應的找出來
      使用 hex 轉 base64 的方法獲取到圖片的 base64 信息,然后在看需要進行轉換
      彩蛋 - 下集預告
      上面說到有一個坑,就是我們獲取的 getData('text/html') 和 getData('text/rtf')

      這 2 個東西并不是憑空出現的,而且人為設置的(不要覺得復制的任何東西都有 text/html)

      這些東西都是在設置剪貼板的時候 setData('text/html')。設置了有什么,才能拿到什么(因為我在富文本的另一個功能中踩到這坑了,包括 safari 瀏覽器也有坑!)

      下一篇文章就來寫寫這個剪貼板的坑!

      復制 word 文檔圖片原理的文章真的好少~希望我這篇能幫到你

      參考文章:http://blog.ncmem.com/wordpress/2023/12/27/%e5%af%8c%e6%96%87%e6%9c%ac%e7%bc%96%e8%be%91%e5%99%a8%e5%a4%8d%e5%88%b6word%e6%96%87%e6%a1%a3%e4%b8%ad%e7%9a%84%e5%9b%be%e7%89%87/

      歡迎入群一起討論

       

       

      posted on 2023-12-27 14:24  Xproer-松鼠  閱讀(829)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 国产在线高清视频无码| 99在线精品国自产拍中文字幕| 好男人视频免费| 国产亚洲AV电影院之毛片| 久艹视频免费看| 国内精品大秀视频日韩精品| 人妻中文字幕在线视频无码| 亚洲性日韩精品一区二区 | 亚洲熟妇乱色一区二区三区| 激情97综合亚洲色婷婷五| 鲁一鲁一鲁一鲁一澡| 亚洲国产性夜夜综合| 乱女乱妇熟女熟妇综合网| 国产精品美女黑丝流水| 国产免费无遮挡吸乳视频在线观看 | www久久只有这里有精品| 67194熟妇在线直接进入| 中文字幕日韩精品人妻 | 国产欧美亚洲精品a| 另类国产精品一区二区| 国产高清自产拍av在线| 国产精品大全中文字幕| 大地资源高清免费观看| 日本va欧美va欧美va精品| 一区二区不卡国产精品| 国产无套内射又大又猛又粗又爽 | 国产一区二区三区不卡观| 国产一区二区三区黄网| 一区二区三区四区亚洲自拍| 成av人电影在线观看| 国产成人午夜在线视频极速观看| 亚洲中文字幕无码永久在线| 日韩精品一区二区在线视| 国产精品中文字幕自拍| 国产精品呻吟一区二区三区| 毛片内射久久久一区| 老熟妇仑乱一区二区视頻| 国产精品一区在线蜜臀| 疯狂做受xxxx高潮欧美日本| 亚洲高清日韩专区精品| 国产精品一区二区三区自拍|