用于将复制内容中的远程图片本地化,防止图片做了防盗链,导致后期无法显示,该插件需要结合后端哦
安装
将代码保存至tinymce/plugins/catchremoteimage/plugin.(min.)js
不存在新建
然后在编辑器配置的plugins
项新增catchremoteimage
加载插件
然后在编辑器新增cri
插件配置
配置
下面上传回调uploader
中使用了jquery
的ajax
,如果你的项目使用其他框架请替换为自己框架的ajax
功能
// 粘贴内容远程图片本地化
cri: {
white_domains: ['cfyun.cc', 'cfyun.top'],
uploader: function(images, success, error) {
if(!images) {
return !1;
}
let data = new FormData();
// 需要本地化的图片地址
data.append('urls', images);
// 额外参数, formData为自定义的参数,例如: { uid: 1, token: 'token string' }
$.each(formData, function(aKey, aVal) {
if(typeof aVal == 'object') { // 如果是object则用jquery的val方法取得内容
aVal = aVal.val();
}
data.append(aKey, aVal);
});
$.ajax({
url: options.uploadUrl,
method: 'post',
data: data,
cache: !1,
contentType: !1,
processData: !1,
forceSync: !1,
beforeSend: function(jqXHR, settings) {
jqXHR.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
jqXHR.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
}
}).then((res) => {
if(res.errno == 0) {
// 在表单内插入已上传附件记录, 用于提交数据保存关系
if(editorImageListElem.length && res.data.list && res.data.list.length) {
$.each(res.data.list, function(k, v) {
editorImageListElem.append('<input type="hidden" name="images[]" value="' + v.aid + '">');
});
}
success(res.data);
return !1;
}
error(res.message || res.data);
}).catch((_error) => {
error(_error || '远程图片本地化失败,请重试!');
});
}
},
还有2个编辑器自带的配置项必须设置
{
paste_data_images: true,
images_file_types: 'jpeg,jpg,png,gif,bmp,webp,svg',
}
插件代码
(function () {
'use strict';
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
var $ = tinymce.util.Tools.resolve('tinymce.dom.DomQuery');
class CatchRemoteImage {
constructor(editor) {
var options = editor.getParam('cri', {}, 'object');
if(!options || !options.uploader) {
return !1;
}
if(!options.white_domains) {
options.white_domains = null;
}
this.images = [];
this.editor = editor;
this.options = options;
let _this = this;
this.event();
return this;
};
/**
* 监听编辑器粘贴
*/
event() {
let _this = this;
// 粘贴远程图片本地化
this.editor.on('paste', function(e) {
let images = [];
const items = _this.editor.getDoc().getElementsByTagName('img');
if(items.length) {
for (var i = 0, img; img = items[i++];) {
if (img.getAttribute('word_img')) {
continue;
}
var src = img.getAttribute('data-src') || img.getAttribute('_src') || img.src || '';
if (/^(https?|ftp):/img.test(src) && !_this.checkDomain(src)) {
images.push(src);
}
}
}
if (images.length) {
_this.uploadImage(images, (data) => {
var i, o, item, res, _src, __src, list = data.list;
for (i = 0; item = items[i++];) {
_src = item.getAttribute('data-src') || item.getAttribute('_src') || item.src || '';
for (o = 0; res = list[o++];) {
if (_src == res.source && res.state == 'success') {// 抓取失败时不做替换处理
__src = res.url;
_this.replaceImage(item, res);
break;
}
}
}
_this.editor.save();
},
(error) => {
_this.editor.notificationManager.open({
text: error || '上传出错,请重试~',
type: 'error',
timeout: 3000,
});
});
}
});
};
// 白名单检测
checkDomain(url) {
if (url.indexOf(location.host) !== -1 || /(^\.)|(^\/)/.test(url)) {
return !0;
}
if (!this.options.white_domains) {
return !0;
}
for (let domain in this.options.white_domains) {
if (this.options.white_domains.hasOwnProperty(domain) && url.indexOf(this.options.white_domains[domain]) !== -1) {
return !0;
}
}
return !1;
};
// 替换原有外部图片为本地化后的图片地址
replaceImage(image, data) {
let _this = this;
const each = function (xs, f) {
for (var i = 0, len = xs.length; i < len; i++) {
var x = xs[i];
f(x, i);
}
};
const map = function (xs, f) {
var len = xs.length;
var r = new Array(len);
for (var i = 0; i < len; i++) {
var x = xs[i];
r[i] = f(x, i);
}
return r;
};
const replaceString = function (content, search, replace) {
let index = 0;
do {
index = content.indexOf(search, index);
if (index !== -1) {
content = content.substring(0, index) + replace + content.substr(index + search.length);
index += replace.length - search.length + 1;
}
} while (index !== -1);
return content;
};
const replaceImageUrl = function (content, targetUrl, replacementUrl) {
let replacementString = 'src="' + replacementUrl + '"';
content = replaceString(content, 'src="' + targetUrl + '"', replacementString);
content = replaceString(content, 'data-mce-src="' + targetUrl + '"', 'data-mce-src="' + replacementUrl + '"');
return content;
};
const replaceUrlInUndoStack = function (targetUrl, replacementUrl) {
each(_this.editor.undoManager.data, function (level) {
if (level.type === 'fragmented') {
level.fragments = map(level.fragments, function (fragment) {
return replaceImageUrl(fragment, targetUrl, replacementUrl);
});
} else {
level.content = replaceImageUrl(level.content, targetUrl, replacementUrl);
}
});
};
let src = this.editor.convertURL(data.url, 'src');
let attr = {
'src': data.url,
'data-mce-src': data.url,
// 'data-app': data.app, // 自定义的img额外属性
// 'data-aid': data.aid // 自定义的img额外属性
};
replaceUrlInUndoStack(image.src, data.url);
$(image).attr(attr).removeAttr('alt');
};
// 将上传
uploadImage(images, success, error) {
// let data = new FormData();
// data.append('urls', images);
if(!this.options.uploader || typeof this.options.uploader !== 'function') {
error('远程图片本地化:上传回调“cri.uploader”配置有误');
return !1;
}
// 回调上传
this.options.uploader(images, success, error);
};
};
function Plugin () {
global.add('catchremoteimage', function (editor) {
editor.on('init', function (e) {
new CatchRemoteImage(editor)
});
});
}
Plugin();
}());