H5 编辑器 Tinymce之解决图片上传/粘贴

TinyMCE 5是一款功能强大且灵活的富文本编辑器,可以嵌入Web应用程序中.

1.在HTML代码中<head>标签中引入下边的代码块

<script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>

*如果使用webpack包管理工具

 npm install tinymce

您也可以通过 tinyMCE 自定义自己所需要的pugins,以满足自己需求的同时,可以减少包的重量

2.在<body>中加入如下HTML代码

  <body><h1>TinyMCE Quick Start Guide</h1><form method="post"><textarea id="mytextarea">Hello, World!</textarea></form></body>

3.新增<script>标签,调用tinymce的init方法

  <script>tinymce.init({selector: '#mytextarea'});</script>

当你引入tinyMCE插件时,全局自动注入tinymce对象,你可以在任何位置去调用它的方法,您可以使用console.log(tinymce)去查看它的所有方法

此时您的页面已初步构建完成,打开浏览器,可以看到如下的功能页面

下面我们就开始完善适合需求的各项功能组件

 window.tinymce.init({selector: `#mytextarea`, //选择器的标识language: "zh", //需要配置的语言//language_url: "./languages/zh_CN.js", //语言配置也可以自己引入自己本地的语言包width:'300px',//设置编辑器高度height: '300px',//设置编辑器的高度font_formats: //显示的自定义字体 以 xx = xx 形式显示"宋体=SimSun;微软雅黑=Microsoft Yahei;华文黑体=STHeiti;华文楷体=STKaiti;华文仿宋=华文仿宋;思源黑体=Source Han Sans CN;思源宋体=Source Han Serif SC;华文细黑=STXihei;黑体=SimHei;方正粗圆简体=方正粗圆简体;Andale Mono=andale mono,times;",fontsize_formats: "8pt 10pt 12pt 14pt 18pt 24pt 36pt",//字体的大小contextmenu://"link image imagetools inserttable | cell row column deletetable | headings",content_css: [ //自定义的content样式表"//fonts.googleapis.com/css?family=Lato:300,300i,400,400i","//www.tinymce.com/css/codepen.min.css"],//skin: "oxide-dark", //皮肤//statusbar: false, //底部状态栏body_class: "panel-body",//给iframe body标签添加class名object_resizing: false,toolbar: [ //工具栏'undo redo |styleselect| bold italic forecolor backcolor | fontselect |fontsizeselect | alignleft aligncenter alignright alignjustify |' +'ht| bullist numlist outdent indent | removeformat|' +'','link image media code codesample hr charmap preview anchor pagebreak insertdatetime me blocks' +'dia emoticons fullscreen save'],menubar: "file edit insert view format table",//顶部操作状态栏plugins: ['advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr image imagetools media insertdatetime link lists nonbreaking noneditable pagebreak powerpaste preview print save searchreplace spellchecker tabfocus table textpattern visualblocks visualchars quickbars charmap'],//所需要的组件数组quickbars_selection_toolbar: //选择文本时的快捷栏"bold italic underline forecolor backcolor | fontselect |fontsizeselect | formatselect | quicklink blockquote",quickbars_image_toolbar: //选中图片时的快捷栏"alignleft aligncenter alignright quicklink |  imageoptions",//external_plugins: { //引入本地的组件包 原包许多组件需要收费,我们自行引入,但需要有资源//   powerpaste: "/public/tinymce/plugins/powerpaste/plugin.min.js"// },formats: { //自定义的alignleft: { //居左自动嵌套div元素,并添加display:flex样式block: "div",styles: { display: "flex", justifyContent: "flex-start" },classes: "left"},aligncenter: {//居中block: "div",styles: {display: "flex",justifyContent: "center"},classes: "center" //添加class名},alignright: {//居右block: "div",styles: { display: "flex", justifyContent: "flex-end" },classes: "right"}},quickbars_insert_toolbar: "quickimage quicktable media", //点击空白区域的快捷工具栏autosave_restore_when_empty: false, //浏览器崩溃自动保存本地end_container_on_empty_block: true,//是否在末尾添加空divpowerpaste_word_import: "merge", //复制粘贴的文字样式处理 参数可以是propmt, merge, clean,效果自行切换对比powerpaste_html_import: "merge", //复制粘贴的html样式处理 propmt, merge, cleanpowerpaste_allow_local_images: true, //复制粘贴图书是否允许本地图片paste_data_images: true,paste_preprocess: (pluginApi, data) => {//console.log(data);// Apply custom filtering by mutating data.content// For example:const content = data.content;const newContent = this.yourCustomFilter(content);data.content = newContent;},code_dialog_height: 450,// code 模态框的高度code_dialog_width: 1000,//code 模态框的宽度charmap_append: [],// advlist_bullet_styles: "circle",//列表样式// advlist_number_styles: "default",//数字列表样式imagetools_cors_hosts: ["www.tinymce.com", "codepen.io"],image_advtab: false,//是否显示图片高级选项// image_uploadtab: false,//是否显示上传图片按钮default_link_target: "_blank",//点击链接是否跳转新页面link_title: false,//链接标题media_live_embeds: true,//想要哪一个图标提供本地文件选择功能,参数可为media(媒体)、image(图片)、file(文件)file_picker_types: "media",//上传文件类型media_alt_source: false,//media_poster: false,//媒体的poster//media_filter_html: false,// file_picker_callback: function(callback, value, meta) {//   //当点击meidia图标上传时,判断meta.filetype == 'media'有必要,因为file_picker_callback是media(媒体)、image(图片)、file(文件)的共同入口//   console.log(meta);//   if (meta.filetype == "media") {//     //创建一个隐藏的type=file的文件选择input//     let input = document.createElement("input");//     input.setAttribute("type", "file");//     input.onchange = function() {//       let file = this.files[0]; //只选取第一个文件。如果要选取全部,后面注意做修改//       console.log(file);//       const url = "";//       let xhr, formData;//       xhr = new XMLHttpRequest();//       xhr.open("POST", self.apiUrl);//       xhr.withCredentials = self.credentials;//       xhr.upload.onprogress = function(e) {//         // 进度(e.loaded / e.total * 100)//         let percent = (e.loaded / e.total) * 100;//         if (percent < 100) {//           tinymce.activeEditor.setProgressState(true); //是否显示loading状态:1(显示)0(隐藏)//         } else {//           tinymce.activeEditor.setProgressState(false);//         }//       };//       xhr.onerror = function() {//         //根据自己的需要添加代码//         tinymce.activeEditor.setProgressState(false);//         return;//       };//       xhr.onload = function() {//         let json;//         if (xhr.status < 200 || xhr.status >= 300) {//           console.log("HTTP 错误: " + xhr.status);//           return;//         }//         json = JSON.parse(xhr.responseText);//         //假设接口返回JSON数据为{status: 0, msg: "上传成功", data: {location: "/localImgs/1546434503854.mp4"}}//         if (json.status == 0) {//           //接口返回的文件保存地址//           let mediaLocation = json.data.location;//           //cb()回调函数,将mediaLocation显示在弹框输入框中//           callback(mediaLocation, { title: file.name });//         } else {//           console.log(json.msg);//           return;//         }//       };//       formData = new FormData();//       //假设接口接收参数为file,值为选中的文件//       formData.append("file", file);//       //正式使用将下面被注释的内容恢复//       xhr.send(formData);//     };//     //触发点击//     input.click();//   }// },media_url_resolver: function(data, resolve) {//上传视频的自定义html代码块,必须调用resole返回代码块try {let videoUri = encodeURI(data.url);let embedHtml = `<p><video controls="controls" width="100%" height="auto">                                                                                                                                                                                                                                             <source src="${videoUri}" type="video/mp4" /></video></p>                                                                                                                                                                                                                                            <p style="text-align: left;">&nbsp;</p>`;resolve({ html: embedHtml });} catch (e) {resolve({ html: "" });}},save_enablewhendirty: true,nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugininit_instance_callback: editor => {//初始化执行代码if (_this.value) {editor.setContent(_this.value);}_this.hasInit = true;//检测编辑器动作editor.on("NodeChange Change KeyUp SetContent", () => {if (this.value !== "") {this.hasChange = true;}this.$emit("input", editor.getContent());});},save_onsavecallback: () => { //点击保存按钮处理函数let content = window.tinymce.get(this.id).getContent();// 匹配并替换 任意html元素中 url 路径if (this.newImgUrl.length) {content = content.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi,(mactch, capture) => {let current = "";console.log(this.newImgUrl);for (let i = 0; i < this.newImgUrl.length; i++) {console.log(capture.replace(/(&amp;)/gi, "&") ==this.newImgUrl[i].originUrl);if (capture.replace(/(&amp;)/gi, "&") ==this.newImgUrl[i].originUrl) {current = this.newImgUrl[i].url;break;}}// this.newImgUrl.forEach(item => {//   console.log(//     item.originUrl == capture.replace(/(&amp;)/gi, "&")//   );//   if (capture.replace(/(&amp;)/gi, "&") == item.originUrl) {//     current = item.url;//   }// });current = current ? current : capture;return mactch.replace(/src=[\'\"]?([^\'\"]*)[\'\"]?/i,"src=" + current);});} // 匹配并替换 img中src图片路径},setup(editor) {editor.on("FullscreenStateChanged", e => {_this.fullscreen = e.state;});},images_upload_handler(blobInfo, success, failure, progress) { //图片上传处理逻辑//此处添加自己的上传图片逻辑代码},urlconverter_callback(url, node, on_save, name) {//自动检测不是自己服务器库的图片//设置白名单const assignUrl = ["blob:http://localhost","data:image/gif;base64"];let curl = url;let isInnerUrl = false; //默认不是内部链接try {assignUrl.forEach(item => {if (url.indexOf(item) > -1) {isInnerUrl = true;throw new Error("EndIterate");}});} catch (e) {if (e.message != "EndIterate") throw e;}if (node == "img" && !isInnerUrl) {let json;getBase64(url).then(base64 => {const url = ``;request({url,file: data2blob(base64, mimes["png"]),filename: "file" + "." + "png"}).then(res => {if (res.data.code == 0) {_this.newImgUrl.push({originUrl: curl,url: res.data.data.url});//return res.data.data.url;//deferred.resolve(res.data.url);} else {failure("Invalid JSON: " + res.data.data.msg);}});});}return url;}});

因为复制粘贴组件powerpaste是收费项目,但又是一个完整编辑器不可或缺的功能,所以这里给出它的下载地址 https://download.csdn.net/download/wddwwwq1/12579334,请自行下载。

在这里我们重点讲解关于powerpaste的功能构建

urlconverter_callback(url, node, on_save, name) {//设置白名单const assignUrl = ["blob:http://localhost","data:image/gif;base64","http://192.168.2.221","http://192.168.2.222","http://192.168.2.223","http://192.168.2.224",];let curl = url;let isInnerUrl = false; //默认不是内部链接try {assignUrl.forEach(item => {if (url.indexOf(item) > -1) {isInnerUrl = true;throw new Error("EndIterate");}});} catch (e) {if (e.message != "EndIterate") throw e;}if (node == "img" && !isInnerUrl) {let json;getBase64(url).then(base64 => {const url =process.env.NODE_ENV !== "production"? "": "";request({url,file: data2blob(base64, mimes["png"]),filename: "file" + "." + "png"}).then(res => {if (res.data.code == 0) {_this.newImgUrl.push({originUrl: curl,url: res.data.data.url});//return res.data.data.url;//deferred.resolve(res.data.url);} else {failure("Invalid JSON: " + res.data.data.msg);}});});}return url;}

上述代码是复制粘贴功能的核心,因为我们复制的图片不一定是自己服务器的图片,所以我们需要把其他服务器的http图片,保存在我们的服务器上。

  • 当编辑器文本中包含有图片首先我们给出一个数组assignUrl,列出您不想让编辑器检测的域名,然后使用回调函数的url去检查是否跟数组白名单的域名相匹配,如果包含,则表示是自己服务器的图片,不需要再上传服务器。

  • urlconverter_callback回调函数有四个参数(url, node, on_save, name),其中url表示检测的链接地址,node则表示此链接所在的标签名字。例如<a href="http://www.baidu.com"></a><img src="https://timgsa.baidu.com/timg.jpg" />

  • 判断node == img && !isInnerUrl,如果为true,则表明我们需要手动上传图片到我们自己的服务器,首先把http格式的图片转换为base64

//图片转换为base64
export default function(img) {function getBase64Image(img, width, height) { //width、height调用时传入具体像素值,控制大小 ,不传则默认图像大小var canvas = document.createElement("canvas");canvas.width = width ? width : img.width;canvas.height = height ? height : img.height;var ctx = canvas.getContext("2d");ctx.drawImage(img, 0, 0, canvas.width, canvas.height);var dataURL = canvas.toDataURL();return dataURL;}var image = new Image();image.crossOrigin = '';image.src = img;//var deferred = $.Deferred();var deferred = new Deferred();if (img) {image.onload = function() {deferred.resolve(getBase64Image(image)); //将base64传给done上传处理}//问题要让onload完成后再return sessionStorage['imgTest']} else {}return deferred.promise;
}
function Deferred() {var self = this;self.promise = new Promise(function(resolve, reject) {self._resolve = resolve;self._reject = reject;});
}Deferred.prototype.resolve = function(data) {this._resolve(data);
}Deferred.prototype.reject = function(data) {//this._reject.call(this.promise,data);this._reject(data);
}

这里使用canvas画图工具转换图片,最后使用canvas.toDataURL()在转换为base64的图片格式

说明:如不了解canvas,请自行学习

  • 因为上传图片是异步,并且上传需要时间,等待服务器返回图片地址时,我们已经把文本显示在编辑器之中,所以在我们return url的时候,还没有得到返回结果,由于tinymce不支持异步处理函数,我尝试用async await处理也没有实际作用,所以我们可以在点击保存文本的时候去手动替换需要替换的图片地址,在上传图片返回结果时我们把图片地址保存在一个数组之中_this.newImgUrl.push({ originUrl: curl, url: res.data.data.url });,以便在保存的时候操作回填。
 save_onsavecallback: () => {let content = window.tinymce.get(this.id).getContent();// 匹配并替换 任意html元素中 url 路径if (this.newImgUrl.length) {content = content.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi,(mactch, capture) => {let current = "";for (let i = 0; i < this.newImgUrl.length; i++) {if (capture.replace(/(&amp;)/gi, "&") ==this.newImgUrl[i].originUrl) {current = this.newImgUrl[i].url;break;}}current = current ? current : capture;return mactch.replace(/src=[\'\"]?([^\'\"]*)[\'\"]?/i,"src=" + current);});} // 匹配并替换 img中src图片路径//...此处上传文本省略},

因为我使用的VUE构建,代码块里的this请自行注意理解


*参考链接地址
【tinyMce官方文档】:https://www.tiny.cloud/docs/

H5 编辑器 Tinymce之解决图片上传/粘贴相关推荐

  1. 微信图片消息 服务器故障,解决图片上传到微信服务器后无法显示问题

    标签:attr   ict   viewport   使用   完全   example   cache   ber   copy vue项目里可以添加到app.vue 关于referrer 在页面引 ...

  2. vue2 + tinymce 包含自定义图片上传及视频、音频上传

    安装 安装时需要注意一个问题,两个包如果不适用会出现报错情况且无法使用,我这边使用的是当前这两个版本 npm install @tinymce/tinymce-vue@3.2.8 -S npm ins ...

  3. wangEditor - 轻量级web富文本编辑器(可带图片上传)

    业务需求: 通过后台编辑文章和图片,上传到前端界面,展示新闻消息模块.这个时候,需要一款简洁的编辑器,百度编辑器是最常用的一种,但是功能太过于复杂,而wangEditor - 轻量级web富文本编辑器 ...

  4. Vue中使用vue-quil-editor富文本编辑器+el-upload实现带图片上传到SpringBoot后台接口

    场景 系统中经常会用到富文本编辑器,比如新增通知和公告功能,并且需要添加上传图片. vue-quill-editor官网: https://www.npmjs.com/package/vue-quil ...

  5. TinyMCE 新增本地图片上传功能

    TinyMCE 新增本地图片上传功能 在TinyMCE 在处理富文本时,可以通过网站的相对路径录入图片地址.这样TinyMCE 就可以正常显示图片了.其实该功能属于普通HTML富文本控件基本功能了.现 ...

  6. 使用百度UMeditor富文本编辑器,修改自定义图片上传,修改源码

    富文本编辑器,不多说了,这个大家应该都用到过,至于用到的什么版本,那就分很多种 CKEditor:很早以前叫FCK,那个时候也用过,现在改名了,比较流行的一个插件,国外很多公司在用 UEDITOR:百 ...

  7. h5页面使用js实现图片上传(安卓用户也可拍摄、相册二选一)

    思路如下: 使用原生input标签实现图片上传时,如果你的写法是如下所示: <input type="file" id="upload_file" @ch ...

  8. ckeditor富文本编辑器的使用和图片上传,复制粘贴图片上传

    项目开发需要用到在线编辑和图片上传,最终讨论使用ckeditor,原因就是其丰富的API.考虑到最新版本ckeditor5可能不够稳定,我们选择使用ckedtior4.9.2版本.官网链接:ckedi ...

  9. 富文本编辑器三种不同图片上传功能

    最近在集成富文本和fastDFS文服做图片上传,找了写相关资料,感觉官网的比较全也比较杂.刚好看到一片 好的文章,然后我就转载并对不太详细的地方进行了一定的修改并新添加了一种前后端分离的方法.各位小伙 ...

最新文章

  1. 不为人知的动网7.1 SQL版注入漏洞
  2. 比特币现金诞生一周年,BCH的未来在哪?
  3. c 调用java包_C#调用java代码(IKVMC)
  4. Linux 访问文件的acl信息,linux文件权限管理与ACL访问控制列表
  5. mysql查看session对应的ip_Mysql 查看session连接数,状态 | 学步园
  6. 收藏!斯坦福Andrew Ng教授“机器学习”26篇教程全译
  7. hdu 3255 Farming(扫描线)
  8. FZU_2019_Mountain Number题解
  9. 小辣椒android密码怎样开,小辣椒手机忘记密码怎么恢复出厂设置
  10. C++ union联合体基础说明及应用
  11. 生成交叉表的SQL基本语句
  12. 自动化测试,你一定要知道的知识
  13. HS6 USB数据采集卡、USB高速数据采集卡,高级触发功能图解
  14. 夸克服务器过载或暂停维修,服务器过载或CGI脚本出错
  15. 电脑连手机热点找不到服务器的ip地址,电脑开热点手机连不上怎么连接
  16. bat之ping操作
  17. 七牛云视频转码 php,学习猿地-我的扩展包分享 - 七牛云视频转码
  18. java反射学习---(框架设计的灵魂,你不收藏都后悔)
  19. Recap | Apache Pulsar Meetup 上海站
  20. Glove模型的原理与代码

热门文章

  1. 单元測试中 Right-BICEP 和 CORRECT
  2. java中获取日期属于哪一年的第几周
  3. 用Python实现简易超市售货系统
  4. 开源实时数据库_实时应用程序的开源数据库
  5. 计算机网络密码凭据,电脑无法上网时总是提示需要输入网络密码如何解决
  6. 工作多年想转行,有哪些正确的方法及技巧呢
  7. 计算机数学基础知识点归纳,计算机数学基础--详细介绍
  8. 快速入门Opentracing-cpp
  9. Linux GCC 编译过程分析及常用检错的编译选项
  10. 树的最大连通分支问题