• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

TinyMCE编辑器粘贴远程图片本地化以及图片拖入上传, 踩坑, 还有上传回调增加自定义属性

互联网 diligentman 6天前 5次浏览

1. 首先说下拖入编辑器上传

采坑说明

拖入上传功能, 是自带的, 我之前以为不行, 自己写了几个小时, 浪费了很多时间

上传后的图片不能插入到拖入的位置, 改了tinymce内部代码后也只能插入到拖入位置的dom节点外层, 最终放弃了, 然后继续啃文档

最终发现了下面的配置项

启用插件paste

首先启用配置paste_data_images默认是false如果需要启用拖入上传则需要设置为true

{
    paste_data_images: true
}

文档中写的是: 此选项指定是否应从粘贴的内容中删除内联图。

但是实际上会把剪切板以及拖入的图片, 用已经配置好的上传接口, 或images_upload_handler重写的上传接口至服务器

允许拖入的图片

这里需要配置images_file_types允许拖入的图片后缀, 如果设置会提示拖入文件不支持

{
    images_file_types: 'jpeg,jpg,png,gif,bmp,webp',
}

好了, 拖拽上传以及剪切板上传设置一下这些配置就好了😂

2. 远程图片本地化

设置初始化结束后执行的回调参数

init_instance_callback : (editor) => {
}

首先检测是否需要上传以及白名单域名

// 设置白名单域名
const localDomains = ['cfyun.cc', 'cfyun.top'];

// 检测是否需要上传
let test = function test(url) {
    if (url.indexOf(location.host) !== -1 || /(^.)|(^/)/.test(url)) {
        return !0;
    }
    // 白名单
    if (localDomains) {
        for (let domain in localDomains) {
            if (localDomains.hasOwnProperty(domain) && url.indexOf(localDomains[domain]) !== -1) {
                return !0;
            }
        }
    }
    return !1;
};

粘贴内容中获取图片

// 粘贴远程图片本地化
editor.on('paste', (e) => {
    let remoteImages = [];
    const doc = editor.getDoc();
    const items = doc.getElementsByTagName('img');

    if(items.length) {
        for (img of items) {
            var src = img.getAttribute('_src') || img.src || '';
            if (/^(https?|ftp):/img.test(src) && !test(src)) {
                remoteImages.push(src);
            }
        }
    }

    if (remoteImages.length) {
        // 需要上传的图片
        CatchRemoteImage(remoteImages, {
            success: (data) => {
                // 这部分代码本来是直接修改编辑器内的dom就可以实现效果的, 但是那种方式无解, 只能现在这样替换原有img的dom才能更新编辑器
                // 貌似编辑器diff验证了dom节点是否相同, 如果相同则没有更新编辑器的内容
                // 希望有大佬能有更好的解决方案
                // 现在的遗憾就是在上传完成后替换为本地的图片后, 有一段加载图片的时间, 这时候编辑器内图片会留白一段时间, 留白时间取决于网速
                let i, o, item, res, _src, __src, list = data.list;
                for (i = 0; item = items[i++];) {
                    _src = item.getAttribute('_src') || item.src || '';
                    for (o = 0; res = list[o++];) {
                        if (_src == res.source && res.state == 'success') {// 抓取失败时不做替换处理
                            __src = res.url;
                            // 无奈之举, 直接修改dom编辑器不更新内容, 但是替换dom可以更新
                            // 这里用到了jquery, 其他框架换个替换dom的方法
                            $(`<img src="${__src}" data-aid="${res.id}" data-width="${res.width}" data-height="${res.height}">`).replaceAll($(item));
                            // 下面给出一种从官方代码中参考来的方式
                            // replaceImage(item, res); // 使用下面的新方法
                            break;
                        }
                    }
                }
                // 同步到textarea
                editor.save();
            },
            error: (data) => {
                // 上传出错回调
            }
        });
    }
});

从tinymce源码中参考的替换图片src的方式

此种方法, 不会造成图片上传之前的留白现象

const replaceImage = (image, data) => {
    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 + '"' + (replacementUrl === 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' ? ' data-placeholder="1"' : '');
        content = replaceString(content, 'src="' + targetUrl + '"', replacementString);
        return content;
    };
    const replaceUrlInUndoStack = function (targetUrl, replacementUrl) {
        each(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 = editor.convertURL(data.url, 'src');
    let attr = {
        'src': data.url,
        'data-app': data.app,
        'data-aid': data.aid
    };
    if(data.db) {
        attr['data-db'] = data.db;
    } else {
        attr['data-width'] = data.width;
        attr['data-height'] = data.height;
    }
    replaceUrlInUndoStack(image.src, data.url);
    editor.$(image).attr(attr).removeAttr('alt').removeAttr('data-mce-src');
}

上传回调方法

/**
 * 远程图片本地化
 */
let CatchRemoteImage = (images, callback)  => {
    const _this = this;

    let data = new FormData();

    // 经过FormData处理后提交给后端的images数组是以,分割的字符串, 这个需要后端自己转为数组再处理
    data.append('urls', images);

    // Append add Form Data
    // formData是需要提交给后端的参数
    $.each(formData, function(aKey, aVal) {
        if(typeof aVal == 'object') { // 如果是object则用jquery的val方法取得内容
            aVal = aVal.val();
        }
        data.append(aKey, aVal);
    });
    // jquery的ajax上传可以替换为其他ajax方法, 或者使用原生XMLHttpRequest方法
    $.ajax({
        url: options.uploadUrl, // 接收远程图片抓去的后端地址
        method: 'post',
        data: data,
        cache: !1,
        contentType: !1,
        processData: !1,
        forceSync: !1,
        beforeSend: function(jqXHR, settings) {
            jqXHR.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        }
    }).then((res) => {
        if(res.errno == 0) {
            callback.success(res.data);
            return !1;
        }
        callback.error(res.data);
    }).catch((error) => {
        callback.error('error');
    });
};

上传成功后的json数据结构

{
    "errno":0,
    "data":{
        "list":[
            {
                "state":"success",
                "url":"//img.cfyun.cc/apps/news/remote/Mon_2103/1616069544468119.png",
                "_url":"//img.cfyun.cc/apps/news/remote/Mon_2103/1616069544468119.png",
                "path":"apps/news/remote/Mon_2103/1616069544468119.png",
                "name":"1616069544468119.png",
                "original":"excalidraw.png",
                "size":84931,
                "ext":".png",
                "is_thumb":0,
                "is_image":1,
                "app":"news",
                "width":1280,
                "height":669,
                "db":52.265625,
                "aid":190,
                "source":"https://gitee.com/kevwan/static/raw/master/doc/images/excalidraw.png"
            }
        ]
    },
    "type":"right",
    "message":"success",
    "referer":"",
    "refresh":false
}

3. 上传图片增加而外属性(需改源码)

官方的成功回调方法可接受参数只有图片链接, 达不到预期效果, 因为要给图片增加一些而外属性, 用于前端

本来打算参考tinymce源码里的回调函数重写一个, 但是发现这部分涉及的代码太多, 不利于重写这个功能

万般无奈下只能修改源码, 下面给出修改的部分, 希望有需要的同学可以参考

首先在搜索handlerSuccess

handlerFailure函数上面

var handlerSuccess = function (blobInfo, url, attr) { // 新增attr
  return {
    url: url,
    blobInfo: blobInfo,
    status: true,
    // 新增attr
    attr: attr
  };
};

继续向下搜索handlerSuccess有2处,在success函数内, 将他改为

var success = function (url, attr) { // 新增attr
  closeNotification_1();
  uploadStatus.markUploaded(blobInfo.blobUri(), url);
  // 这里修改增加了attr
  resolvePending(blobInfo.blobUri(), handlerSuccess(blobInfo, url, attr));
  resolve(handlerSuccess(blobInfo, url, attr));
};

搜索replaceImageUriInView第一个函数修改如下

var replaceImageUriInView = function (image, resultUri, _attr) { // 新增_attr
  var src = editor.convertURL(resultUri, 'src');
  replaceUrlInUndoStack(image.src, resultUri);
  var attr = {
      'src': shouldReuseFileName(editor) ? cacheInvalidator(resultUri) : resultUri,
      'data-mce-src': src,
  };
  // 这里修改来原来的代码增加了属性添加
  if(typeof attr == 'object') {
    attr = Tools.extend(attr, _attr);
  }
  editor.$(image).attr(attr);
};

搜索第二个replaceImageUriInView改为

replaceImageUriInView(image, uploadInfo.url, uploadInfo.attr);

好了, 如果你没有改错代码,

在自定义上传回调函数内更改

success增加第二个参数attr如下

success(res.url, {
    'data-id': res.id,
    'data-width': res.width,
    'data-height': res.height
});

修改完毕👏

现在可以测试一下, 拖入上传以及正常选择上传是否会在img标签增加你而外配置的属性

修改源码后压缩请看另一篇文章😸

✅ 使用UglifyJS压缩混淆js

至此粘贴远程图片本地化就完成了, 希望本文能帮到你, 让你避免长时间的浪费在这个功能上🧲

展开阅读全文

tinymceuglifyjsjavascript

© 著作权归作者所有

举报

打赏

0


0 收藏

微信
QQ
微博

分享

作者的其它热门文章

用PHP+JS为你的文章(文档)打造目录树
使用UglifyJS压缩混淆js
thinkphp6的filesystem上传驱动集合包含oss,qiniu,cos
Tinymce编辑器工具栏吸顶插件,支持设置顶部预留位置,及focus显示blur隐藏


喜欢 (0)