UEditor 之初体验后记

  • 1、UEditor 基本介绍

    • 1.1、关于 UEditor
    • 1.2、UEditor 现状
  • 2、UEditor 简单使用
    • 2.1、将 UEditor 源码集成到项目中
    • 2.2、让 UEditor 的 UI 呈现在页面中
    • 2.3、用 UEditor 的过程中遇到的两个坑
    • 2.4、将 UEditor 中的图片上传到云服务器上
  • 3、Web 编辑器杂谈
    • 3.1、我与 UEditor 的曲折历程
    • 3.2、CKEditor 空格变问号的原因及解决办法

本文初稿写于 3 年前,近期重新编辑整理,并进一步完善补充而成。

1、UEditor 基本介绍

1.1、关于 UEditor

UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码。

上面这句话出自 UEditor 官方。在我看来,UEditor 的核心特点就是:产自大厂、开源免费、功能全面(相当全)、体验较为切合国人习惯。想了解 UEditor 的更多信息,可参考以下几个链接:

另外,UEditor 官方还提供了一个 Mini 版,名字叫 UMeditor。说是为了满足广大门户网站对于简单发帖框,或者回复框需求所定制的版本。主要改进点在加载速度和加载失败率上,且功能与 UEditor 也略有不同,具体可参考 UMeditor 的 GitHub

1.2、UEditor 现状

UEditor 的最新版本是 v1.4.3.3,UMEditor 的最新版本是 v1.2.3。最后一次发布都在 2016 年下半年,都快 3 年没更新了。不过基本功能该有的也都有,且社区也比较认同和推崇,有不少人或组织都提供了特色的改进版或增强版,官方也偶有跟进。

综合来看,目前要在国内做相对复杂的资讯发布或留言功能,UEditor/UMEditor 依然是最好的选择之一。

2、UEditor 简单使用

2.1、将 UEditor 源码集成到项目中

如果你要在后端项目中集成 UEditor,那么直接下载对应语言版本的源码,解压之后拷贝到项目中即可。有两点需要注意:

  • 1、官方只提供了 PHP、ASP、ASP.NET、JSP 四个版本,社区里有人提供了 Note.js、Python 等语言的版本。
  • 2、官方说提供的所有后端代码都仅为 DEMO 作用,切不可直接用到生产环境中。平心而论,ASP.NET 版的后端代码写的确实水,不过前端能写出这些后端代码就已经很牛掰了,我是把用到的那部分代码从头到尾改了一遍才用到生产环境中的。

如果你要在前端项目中集成 UEditor,那就看你需不需要上传图片等后端功能了。如果不需要,那么直接拷贝到前端项目中即可。但如果需要,那就得确保跑前端的 Web 服务器能解析对应的后端代码了。

当然,如果你把 UEditor 单独部署到一个支持后端的 Web 服务器上也是可行的。另外,集成 UMeditor 的方法与集成 UEditor 方法完全一样。

2.2、让 UEditor 的 UI 呈现在页面中

1、首先,需要在页面中引入 UEditor 的 3 个 js 文件,示例如下:

<!-- 编辑器的配置文件,前端配置都在这个文件中,注释十分丰富 -->
<script type="text/javascript" charset="utf-8" src="/ueditor-1.4.3/ueditor.config.js"></script>
<!-- 编辑器的源码文件 -->
<script type="text/javascript" charset="utf-8" src="/ueditor-1.4.3/ueditor.all.min.js"></script>
<!-- 编辑器的中文语言包 -->
<script type="text/javascript" charset="utf-8" src="/ueditor-1.4.3/lang/zh-cn/zh-cn.js"></script>

2、然后,在页面中需要呈现 UEditor 的地方放置如下“占位符”代码(注意给个 ID,下一步要用):

<!-- 编辑器的容器 -->
<script id="editor" type="text/plain" style="width:800px; height:300px;"></script>

3、最后,通过一句实例化编辑器的代码来获得编辑器实例(页面运行起来时 UI 就会呈现出来):

$(function () {
    var ue = UE.getEditor("editor"); // 创建编辑器实例(这里的 editor 就是上一步中的 ID)
});

得到编辑器实例对象之后,就可以通过它来调用 UEditor 的哪些 API 了。具体有哪些 API 可用,参见 UEditor API

2.3、用 UEditor 的过程中遇到的两个坑

1、坑一,获取内容可以而设置内容却报错

我用getContent获取编辑器的内容并存入数据库,然后把存到数据库中的内容用setContent赋给编辑器。结果运行起来发现保存入库可以,而打开编辑页面直接就报错了,编辑器也没显示出来。

后来发现这个是因为编辑器还没准备好,所以就不能赋值,只需要把赋值操作放到ready之后即可,示例代码如下:

ue.addListener("ready", function () {
    ue.setContent("@Model.Content"); // 等编辑器准备好之后再赋值,否则报错
});

为了写这篇文章,我尝试了重现那个错误,结果编辑器又显示出来了,但依然会报错,错误消息是“Uncaught TypeError: Cannot set property ‘innerHTML‘ of undefined”。

从某种程度上说,这个问题并不算是坑。因为官方手册中就有相关说明和建议——“对编辑器的操作最好在编辑器ready之后再做”,不过我这人特别怕麻烦,凡是能凭猜测搞定的就不愿细看文档,所以第一次用的时候就掉坑了。

2、坑二,编辑器内显示的是的 HTML,而不是文本内容

一开始还怀疑这会不会是 UEditor 的 Bug,直到把这个问题解决之后,反过来想才发现这里面其实隐含了一个容易被忽略的“常识”,那就是网页的模版引擎一般都会编码字符串,比如我曾用过的 WebForms、NVelocity 和 RazorEngine 都是这样。

换句话说,这时候编辑器拿到的是经过编码之后 HTML,也就是说只要我们能确保编辑器拿到的是编码前的 HTML 就行了。当然一开始我还没想到是这个原因,于是我的解决办法是根据曾使用其它编辑器的经验试出来的,就是直接把内容放到script标签中,示例代码如下:

<script id="editor" type="text/plain" style="width:800px;height:300px;">
    @WebTools.RazorHelper.Raw(Model.Segment.Note)
</script>

在我意识到这个问题的始作俑者是网页模版引擎而不是 UEditor 的时候,我做了如下测试:

ue.addListener("ready", function () {
    editor.setContent("@WebTools.RazorHelper.Raw(Model.Segment.Note)");
});

果然也是 OK 的,这也进一步证明了我的猜想是正确的!

2.4、将 UEditor 中的图片上传到云服务器上

由于 UEditor 默认是将图片上传到网站根目录下的一个子文件夹中,如果是企业内部的信息系统,这么做也还行,但如果是互联网项目,这种做法就显得既不安全、也不经济的了,最好还是上传到云服务器上。

要把 UEditor 中的图片上传到云服务器上,只需要修改后端代码的上传逻辑即可。我做过用 ASP.NET 把 UEditor 1.4.3 中的图片上传到 又拍云 USS 和把 UMeditor 1.2.2 中的图片上传到 阿里云 OSS,其实思路是一样的。

UEditor 的 ASP.NET 核心上传逻辑在UploadHandler类的Process方法中,将上传图片到本地的那部分代码替换成如下示例代码即可。

try {
    // 校验文件类型、文件大小等......
    String fileMd5 = StringSecurity.GetMd5(uploadFileBytes); // 用文件的 MD5 值做文件名
    String fileName = fileMd5 + Path.GetExtension(uploadFileName);
    String filePath = $"/images/{DateTime.Now.ToString("yyMMdd")}/{fileName}";
    // 调用又拍云的 SDK 来上传文件
    UpYun upyun = new UpYun("bucketname", "username", "password");
    upyun.setContentMD5(fileMd5);
    bool upyunResult = upyun.writeFile(filePath, uploadFileBytes, true);
    // ......
} catch (Exception e) {
    logger.Error("文件上传失败!", e);
    // ......
}

UMeditor 的 ASP.NET 核心上传逻辑在Uploader类的upFile方法中,将上传图片到本地的那部分代码替换成如下示例代码即可。

try {
    // 校验文件类型、文件大小等......
    String fileName = $"{GuidHelper.LowerPureString}.{getFileExt()}";
    String filePath = $"images/{DateTime.Now.ToString("yyMMdd")}/{fileName}";
    // 调用阿里云的 SDK 来上传文件
    var client = new OssClient(SrcPoint, AccessKeyId, AccessKeySecret,
        new Aliyun.OSS.Common.ClientConfiguration() { IsCname = true });
    var putResult = client.PutObject(SrcBucket, filePath, uploadFile.InputStream);
    // ......
    var uri = client.GeneratePresignedUri(SrcBucket, filePath);
    URL = uri.GetLeftPart(UriPartial.Path); // 将文件路径赋值给编辑器引用的变量
} catch (Exception e) {
    logger.Error("文件上传失败!", e);
    // ......
}

可能你看到本文时,又拍云和阿里云的云存储 SDK 已经做了较大改动,这里附上二者的 GitHub 链接:

3、Web 编辑器杂谈

3.1、我与 UEditor 的曲折历程

2016 年初,我基于学习的目的,业余做了一个带新闻发布功能的网站。在这之前,我曾先后用过 FreeTextBoxFCKeditor(已更名为 CKEditor),印象中都还不错,但也都有着这样或者那样的问题,比如 CKEditor 中空格变问号的问题

我上网搜了一下,发现大家对 UEditor 的评价还不错,于是决定 Web 编辑器就用 UEditor,由于是第一次用,过程相对曲折。做完新闻发布功能之后,就顺带记录了一下,也就是本文初稿。

两年之后,也就是去年,公司要做个带资讯发布功能的媒体网站,在我的建议下,最终选用了 UMeditor。但整个团队除我之外都没用过,尤其是那几个前端,连富文本编辑器是啥都不知道,最后就由我负责带领前端来做这块儿的功能。

因为这次做的是实际项目,公司的编辑们都在用,后台的 UI 框架也由 jQuery 版的 EasyUI 变成了 React 版的 Ant Design,所以遇到的细节问题也比较多。其中对编辑器的各种配置和后端改造,都是由我统一解决好再跟前端对接的。

又过了一年,也就是本月初,我在整理资料时再次看到了本文初稿。瞬间就想起我去年用 UMeditor 的过程中也做过一些记录,然后就想着干脆把两次的记录整合起来,结果怎么都找不到去年的记录,只好作罢。也许还留在那家公司的电脑里吧!

3.2、CKEditor 空格变问号的原因及解决办法

经查,导致该问题的根本原因是编码转换。在 UTF-8 里有一个特殊的编码0xC2 0xA0,转换成字符的时候,表现为一个空格,跟普通的半角空格一样,不同的是它的宽度不会被压缩,因此常被用来做网页排版。而在 GB2312、Unicode 等字符集中却没有这个编码,因此如果简单地进行编码转换,这个编码就会被替换成问号。

前些年在实际项目中还遇到过更奇葩的情况,文章保存之后,内容中的问号就全都没了。后来发现是别人也遇到了空格变问号的问题,但选错了解决方案,他是直接把问号又替换成空格,结果正常的问号也被毙掉了。

正确的做法是,用 UTF-8 格式的编码进行替换,把那个特殊的空格替换为普通的空格,如果是 HTML 字符串,那就替换为&nbsp;。C# 代码替换 HTML 字符串的示例如下:

byte[] space = new byte[]{0xc2,0xa0};
string utfSpace = Encoding.GetEncoding("UTF-8").GetString(space);
htmlStr = htmlStr.Replace(utfSpace,"&nbsp;");

注意:在替换之前不能进行编码转换,一定要继续使用 UTF-8 编码。如果已经转换成其它编码,那就彻底没救了,因为这时候错误的问号和正常的问号之间已经没有分别了。

本文链接http://www.cnblogs.com/hanzongze/p/js-ueditor.html
版权声明:本文为博客园博主 韩宗泽 原创,作者保留署名权!欢迎通过转载、演绎或其它传播方式来使用本文,但必须在明显位置给出作者署名和本文链接!个人博客,能力有限,若有不当之处,敬请批评指正,谢谢!

原文地址:https://www.cnblogs.com/hanzongze/p/js-ueditor.html

时间: 2025-01-16 20:03:11

UEditor 之初体验后记的相关文章

Microsoft IoT Starter Kit 开发初体验

1. 引子 今年6月底,在上海举办的中国国际物联网大会上,微软中国面向中国物联网社区推出了Microsoft IoT Starter Kit ,并且免费开放1000套的申请.申请地址为:http://aka.ms/iotkits,目前仍然有效.当时一开放申请,我就在线填写了申请表,接下来就是长长的等待.相信很多朋友都是一样,在经过几个月的等待之后,终于拿到了这个开发套件,而有些朋友估计还在等待中.因为官方是一个月处理并邮寄一批,速度不是很快.但是,在经过了一段时间使用以后,我可以说,如果朋友们期

erlang 初体验

最近测试了一下 erlang的坑... 如不出意外.... 大家第一眼看到这语法... 心里第一句一定是"我擦.这TM都是啥!!!!!" 没有变量!!! 没有结构体!!! 没有循环!!! 好吧,至少我是这样想的. 找了半天..连个if也不知道怎么写.. 这记录一些基本常识.. -module(module_name)  %%定义模块 括号内的要和文件名相同. -export([fun1/1 fun2/2]) %%这里是导出2个函数对外使用  函数名/参数名. 一个简单的函数定义如下 f

linux初体验

第一次听到linux这个'词语'是在一次偶然的朋友聊天中朋友提到的,之前压根没听到过'这个东西',所以我可以说是个linux的新新手,菜鸟都不算. 截至到目前,我已经开始linux系统运维学习有差不多10天时间了.在没接触linux之前,我对它的认识仅仅是:它是个计算机系统.决定学习linux系统运维之前,自我以为运维应该是对系统的一些日常维护之类的,不会很难的东西,我更希望运维是个不难的东西,我个人很笨,对难的东西可能接受的很慢,所以我愿意认为运维是很简单的,这样我就可以轻轻松松的掌握运维相关

【Spark深入学习 -15】Spark Streaming前奏-Kafka初体验

----本节内容------- 1.Kafka基础概念 1.1 出世背景 1.2 基本原理 1.2.1.前置知识 1.2.2.架构和原理 1.2.3.基本概念 1.2.4.kafka特点 2.Kafka初体验 2.1 环境准备 2.2 Kafka小试牛刀 2.2.1单个broker初体验 2.2.2 多个broker初体验 2.3 Kafka分布式集群构建 2.3.1 Kafka分布式集群构建 2.3.2 Kafka主题创建 2.3.3 生产者生产数据 2.3.4消费者消费数据 2.3.5消息的

Java8初体验(二)Stream语法详解

原文链接:http://ifeve.com/stream/ 1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel aggregate operations. 我们来解读一下上面的那句话: Stream是元素的集合,这点让Stream看起来用些类似Iterator: 可以支持顺序和并行的对原Stream进行汇聚的操作: 大家可以把Stream当成一个高级版本的

hibernate--CRUD初体验

hibernate的crud操作初体验. 看具体实例 package com.fuwh.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import org.hibernate.annotations.GenericGenerator; @Entity publ

Oracle SQL篇(一)null值之初体验

    从我第一次正式的写sql语句到现在,已经超过10年的时间了.我写报表,做统计分析和财务对账,我一点点的接触oracle数据库,并尝试深入了解.这条路,一走就是10年,从充满热情,到开始厌倦,我不知道我还能坚持多久,未来的路,谁知道呢? 也许是该抓紧时间,做一点什么了,我不知道该开始写些什么,我从来没有在网上写东西的习惯.     先从简单的开始吧,那当然就是SQL,这是我SQL系列的第一篇,希望我能够坚持. 在Oracle数据库中,如果一个表中的列没有值的话,我们可以说是空值,比如IT员

AngularJS路由系列(3)-- UI-Router初体验

本系列探寻AngularJS的路由机制,在WebStorm下开发. AngularJS路由系列包括: 1.AngularJS路由系列(1)--基本路由配置2.AngularJS路由系列(2)--刷新.查看路由,路由事件和URL格式,获取路由参数,路由的Resolve3.AngularJS路由系列(3)-- UI-Router初体验4.AngularJS路由系列(4)-- UI-Router的$state服务.路由事件.获取路由参数5.AngularJS路由系列(5)-- UI-Router的路由

Android Studio初体验之启动AVD模拟器异常:cannot set up guest memory &#39;pc.ram&#39;

启动AVD模拟器异常:Cannot set up guest memory 'pc.ram' 错误信息: HAX is working and emulator runs in fast virt mode Cannot set up guest memory 'pc.ram': Invalid argument Error accepting connect 分析 各种查资料,没有发现网上有同样问题的,在一篇相关文章中找到类似的解决方法. 从语意看,应该是hax安装后没有启动.(不懂hax是什