UEditor + 秀米編輯器的集成 + 編輯器富文本圖片的轉存

場景&需求:也是源于一個需求。本來我們的后臺系統的編輯器用的是tinymce。后來由于運營那邊都是用別的第三方編輯器去編輯各種版式的文章,然后再復制過來后臺系統這邊發布,由于發布文章也是展示在移動端上的,然后就會有不少問題。

(好像這里展示不了代碼,看代碼的話可以打開我的原文鏈接:《UEditor + 秀米編輯器的集成 + 編輯器富文本圖片的轉存》

**1:文章的排版錯誤,發布出去的內容排版和秀米編輯器上面的相差太遠**

**2:由于在秀米編輯器那邊發布的文章,上傳的圖片都是存在他們的服務器,然后我們后臺發布后就出現了防盜鏈顯示不出來的情況,就更是加劇了排版錯亂**

**解決方案:讓在秀米編輯器復制過來的文檔在我們的后臺系統發布時候排版正常,已經,處理防盜鏈的問題,就是將第三方的圖片轉存到我們自己的服務器或者oss**



然后發現,秀米官方有提供給第三方的對接方案。我們這邊系統需要有自己的編輯器,畢竟有時候也是會有不需要花里胡哨排版的時候的,所以,就采用了秀米官方提供的第二個方案,但是有個前提,必須是UEditor的內核。那就是說,我們就要采用UEditor的編輯器了。




?一、引入UEditor

1、因為我們后臺用的技術棧是vue,就采用了大佬封裝好的[vue-ueditor-wrap](https://github.com/HaoChuan9421/vue-ueditor-wrap/tree/2.x)。然后按照它的說明來安裝,npm安裝好包后,按照說明去[UEditor 官網](http://fex.baidu.com/ueditor/#start-start)下載UEditor文件夾,放在項目的靜態資源文件夾下就行。


2、引入組件 & 注冊

....... 這里略過了~這里的配置就按照github上面一步步配置就行了,最后最重要是編輯器配置的UEDITOR_HOME_URL 和 serverUrl兩個選項。具體可以看UEditor文檔[UEditor文檔](http://fex.baidu.com/ueditor/#start-config)

-----------

二、引入秀米,集成進UEditor

這里是官網的引入實例,[秀米圖文排版UEditor插件示例](https://ent.xiumi.us/ue/)

按照他的文檔引入就行了,這里沒有什么坑。有個小坑后面在圖片轉存的時候才需要去搞。

這一步引入沒啥問題的話,編輯器的工具欄就會展示出來一個秀米的小圖標在最后面了。

![image.png](/upload/2022/01/image-0167cd1c6cf74d39ba52a4707318f6b0.png)

------------

## 三、圖片轉存,這里的就都是代碼了


UEditor是有提供了一個圖片轉存的選項,只需要在他的代碼里面做一下轉存的更改就可以了。

然后就是去到它的源碼文件那里,大概是在23200行左右,可能每個版本不一樣,找到這個地方,里面的監聽catchRemoteImage函數里面


我這邊是重寫了它原來的邏輯,因為我們這邊的轉存是前端獲取到圖片地址,然后請求到后端,處理完返回轉存后的oss地址,我這邊再去做替換原來的地址,就是替換掉原來秀米的oss地址。




```javascript

me.addListener("catchRemoteImage", function () {

? ? ? ? var catcherLocalDomain = me.getOpt('catcherLocalDomain') || [],

? ? ? ? ? ? catcherActionUrl = me.getActionUrl(me.getOpt('catcherActionName')),

? ? ? ? ? ? catcherUrlPrefix = me.getOpt('catcherUrlPrefix'),

? ? ? ? ? ? catcherFieldName = me.getOpt('catcherFieldName');

? ? ? ? ? ? try {

? ? ? ? ? ? ? catcherLocalDomain.push('k-mmh.com')? //插入不需要過濾的域名白名單? 2022年1月19日? --kapok? ***************************************

? ? ? ? ? ? } catch (error) {


? ? ? ? ? ? }

? ? ? ? ? ? console.log('白名單',catcherLocalDomain)

? ? ? ? ? ? // 重寫xhr請求? 不改動原來ueditor的了,防止出別的問題? ? ? ? ? ? --kapok

? ? ? ? ? ? var kapokHttp = {};

? ? ? ? ? ? kapokHttp.quest = function (option, callback) {

? ? ? ? ? ? ? var url = option.url;

? ? ? ? ? ? ? var method = option.method;

? ? ? ? ? ? ? var data = option.data;

? ? ? ? ? ? ? var timeout = option.timeout || 0;

? ? ? ? ? ? ? var xhr = new XMLHttpRequest();

? ? ? ? ? ? ? (timeout > 0) && (xhr.timeout = timeout);

? ? ? ? ? ? ? xhr.onreadystatechange = function () {

? ? ? ? ? ? ? ? if (xhr.readyState == 4) {

? ? ? ? ? ? ? ? ? if (xhr.status >= 200 && xhr.status < 400) {

? ? ? ? ? ? ? ? ? ? var result = xhr.responseText;

? ? ? ? ? ? ? ? ? ? try { result = JSON.parse(xhr.responseText); } catch (e) { }

? ? ? ? ? ? ? ? ? ? callback && callback(null, result);

? ? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? callback && callback('status: ' + xhr.status);

? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? }.bind(this);

? ? ? ? ? ? ? xhr.open(method, url, true);

? ? ? ? ? ? ? if (typeof data === 'object') {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? data = JSON.stringify(data);

? ? ? ? ? ? ? ? } catch (e) { }

? ? ? ? ? ? ? }

? ? ? ? ? ? ? // 獲取token

? ? ? ? ? ? ? let getTheCookie = function(c_name){

? ? ? ? ? ? ? ? if(document.cookie.length > 0) {

? ? ? ? ? ? ? ? ? let c_start = document.cookie.indexOf(c_name + "=");//獲取字符串的起點

? ? ? ? ? ? ? ? ? if(c_start != -1) {

? ? ? ? ? ? ? ? ? ? c_start = c_start + c_name.length + 1;//獲取值的起點

? ? ? ? ? ? ? ? ? ? let c_end = document.cookie.indexOf(";", c_start);//獲取結尾處

? ? ? ? ? ? ? ? ? ? if(c_end == -1) c_end = document.cookie.length;//如果是最后一個,結尾就是cookie字符串的結尾

? ? ? ? ? ? ? ? ? ? return decodeURI(document.cookie.substring(c_start, c_end));//截取字符串返回

? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }


? ? ? ? ? ? ? }

? ? ? ? ? ? ? // 這里由于上傳文件轉存地址接口需要用到token,也是直接寫死? ---- 可以增加判斷是否需要

? ? ? ? ? ? ? xhr.setRequestHeader('UserKey', getTheCookie('vue_admin_template_token'));

? ? ? ? ? ? ? // 這里接口是post,直接寫死Content-Type,如果有新的接口可以修改判斷去配置

? ? ? ? ? ? ? xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');


? ? ? ? ? ? ? xhr.send(data);

? ? ? ? ? ? ? xhr.ontimeout = function () {

? ? ? ? ? ? ? ? callback && callback('timeout');

? ? ? ? ? ? ? ? console.log('%c連%c接%c超%c時', 'color:red', 'color:orange', 'color:purple', 'color:green');

? ? ? ? ? ? ? };

? ? ? ? ? ? };

? ? ? ? ? ? // get用不到,注釋了 ----------------------------------

? ? ? ? ? ? // kapokHttp.get = function (url, callback) {

? ? ? ? ? ? //? var option = url.url ? url : { url: url };

? ? ? ? ? ? //? option.method = 'get';

? ? ? ? ? ? //? this.quest(option, callback);

? ? ? ? ? ? // };

? ? ? ? ? ? kapokHttp.post = function (option, callback) {

? ? ? ? ? ? ? option.method = 'post';

? ? ? ? ? ? ? this.quest(option, callback);

? ? ? ? ? ? };

? ? ? ? ? ? // 嘗試下重寫原來editor的圖片替換? ----- start ----------------------------------------

? ? ? ? ? ? var remoteImages = [],


? ? ? ? ? ? //獲取富文本里style里帶url的元素,以及img元素

? ? ? ? ? ? imgs = me.document.querySelectorAll('[style*="url"],img'),


? ? ? ? ? ? //判斷圖片鏈接是否在白名單內的方法? ? --白名單要在上面的白名單配置,配置我們的oss地址前綴,不然觸發這個方法的時候會無限次去換新圖

? ? ? ? ? ? test = function(src, urls) {

? ? ? ? ? ? ? if (src.indexOf(location.host) != -1 || /(^\.)|(^\/)/.test(src)) {

? ? ? ? ? ? ? ? return true;

? ? ? ? ? ? ? }

? ? ? ? ? ? ? if (urls) {

? ? ? ? ? ? ? ? for (var j = 0, url; (url = urls[j++]); ) {

? ? ? ? ? ? ? ? ? if (src.indexOf(url) !== -1) {

? ? ? ? ? ? ? ? ? ? return true;

? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? }

? ? ? ? ? ? ? return false;

? ? ? ? ? ? };

? ? ? ? ? ? for (var i = 0, ci; i<imgs.length;i++) {

? ? ? ? ? ? ? ci = imgs[i]

? ? ? ? ? ? ? //ci為current item,當前元素

? ? ? ? ? ? ? // 這里臨時存一個新的變量

? ? ? ? ? ? ? let cc = ci

? ? ? ? ? ? ? //如果有word_img這個屬性的話,就略過-------------------------

? ? ? ? ? ? ? if (cc.getAttribute("word_img")) {

? ? ? ? ? ? ? ? continue;

? ? ? ? ? ? ? }

? ? ? ? ? ? ? //如果該元素是一img元素

? ? ? ? ? ? ? if(cc.nodeName == "IMG"){

? ? ? ? ? ? ? //獲取圖片元素的src的值,估計會有“”空字符串,因為有的圖片確實是沒有寫src鏈接的

? ? ? ? ? ? ? ? var src = cc.getAttribute("_src") || cc.src || "";

? ? ? ? ? ? ? ? //判斷頭部是否含有https、http或者ftp字樣,并且不在白名單里面的

? ? ? ? ? ? ? ? if (/^(https?|ftp):/i.test(src) && !test(src, catcherLocalDomain)) {

? ? ? ? ? ? ? ? ? // 把圖片存起來、、、、不過這個可能暫時不用了,預留后面的奇葩需求,放著...........

? ? ? ? ? ? ? ? ? remoteImages.push(src);?

? ? ? ? ? ? ? ? ? // 接口取到

? ? ? ? ? ? ? ? ? let ajaxUrl = `${process.env.VUE_APP_BASE_API}/api/admin/v1/file/uploadFileByUrl`

? ? ? ? ? ? ? ? ? kapokHttp.post({ url: ajaxUrl, data: `fileUrl=${src.split('?')[0]}`, timeout: 60000 }, function (err, res) {

? ? ? ? ? ? ? ? ? ? ? // 這里對結果進行處理,替換

? ? ? ? ? ? ? ? ? ? ? if(res.status === 'success'){

? ? ? ? ? ? ? ? ? ? ? ? domUtils.setAttributes(cc, {

? ? ? ? ? ? ? ? ? ? ? ? ? class: "newUrlClass",

? ? ? ? ? ? ? ? ? ? ? ? ? "src": res.result,

? ? ? ? ? ? ? ? ? ? ? ? ? "_src": res.result

? ? ? ? ? ? ? ? ? ? ? ? })

? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? });

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? // 這里不是圖片就是背景圖了

? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? // 獲取背景圖片url

? ? ? ? ? ? ? ? var backgroundImageurl = cc.style.cssText.replace(/.*\s?url\([\'"]?/, '').replace(/[\'"]?\).*/, '');

? ? ? ? ? ? ? ? //跟上面的img差不多的判斷

? ? ? ? ? ? ? ? if (/^(https?|ftp):/i.test(backgroundImageurl) && !test(backgroundImageurl, catcherLocalDomain)) {

? ? ? ? ? ? ? ? ? // 跟上面一樣,把圖片存起來、、、、不過這個可能暫時不用了,預留后面的奇葩需求,放著...........

? ? ? ? ? ? ? ? ? remoteImages.push(backgroundImageurl);

? ? ? ? ? ? ? ? ? // 接口地址

? ? ? ? ? ? ? ? ? let ajaxUrl = `${process.env.VUE_APP_BASE_API}/api/admin/v1/file/uploadFileByUrl`

? ? ? ? ? ? ? ? ? let newRequestUrl = backgroundImageurl

? ? ? ? ? ? ? ? ? kapokHttp.post({ url: ajaxUrl, data: `fileUrl=${newRequestUrl.split('?')[0]}`, timeout: 60000 }, function (err, res) {

? ? ? ? ? ? ? ? ? ? ? // 這里對結果進行處理

? ? ? ? ? ? ? ? ? ? ? if(res.status === 'success'){

? ? ? ? ? ? ? ? ? ? ? ? cc.style.cssText = cc.style.cssText.replace(backgroundImageurl, res.result);

? ? ? ? ? ? ? ? ? ? ? ? domUtils.setAttributes(cc, {

? ? ? ? ? ? ? ? ? ? ? ? ? "data-background": res.resul

? ? ? ? ? ? ? ? ? ? ? ? })

? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? });

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? }

? ? ? ? ? }

}

```

然后最后還有一個一個秀米的坑,在他們官網下載的這個xiumi-ue-dialog-v5.html文件,在完成編輯點導出的時候是沒有觸發UEditor的這個遠程圖片抓取函數的,需要增加上一句代碼,因為這個遠程圖片抓取是UEditor那邊粘貼事件觸發的。

```language

editor.fireEvent('catchRemoteImage');

```


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容