资讯动态

UEditor 1.4.3集成实战:前端配置、PHP上传与安全过滤

发布时间:2026/10/11 11:50:37 来源:尧图企业网站定制
简介这份压缩包对应百度 UEditor 1.4.3 富文本编辑器发行版定位为网站后台、内容管理系统与论坛中常用的在线编辑组件适合前后端开发者直接部署或二次定制。资源整体约2.72MB共277个文件覆盖主要运行形态76个脚本文件实现编辑器初始化、命令交互与插件扩展87张图片和45张动画图提供图标、皮肤与状态反馈20个样式表控制外观主题与代码高亮26个网页示例便于快速上手另有PHP接口脚本、SWF兼容组件与多种字体文件支撑上传、排版等场景。目前已有719人学习下载。解压后可对照官方示例配置编辑器参数查看源码目录理解模块划分还能根据项目需要调整工具栏按钮、新增自定义插件或改造后端上传逻辑这份资源对研究轻量级富文本编辑器的前端架构和文件组织方式也有直接参考价值。1. 富文本编辑器 ueditor1.4.3这个老版本凭什么还能打做后台管理系统的人十有八九绕不开富文本编辑器。市面上的轮子换了一代又一代但很多老项目、企业内部系统、外包交付的代码里用的还是 ueditor1.4.3 这个版本。它不是最新却胜在成熟解压就能跑PHP 后端接口齐全工具栏、图片上传、视频上传、代码高亮全都内置。这套东西的定位很直接——给网页里嵌一个能传图、能排版、能存 HTML 的编辑框前后端联调半小时内能通。适合谁适合要给管理后台加编辑器、又不想从零写一套上传和渲染逻辑的开发者。这篇就把我拆这个包时踩过的坑、改过的参数、验证过的流程一次说清。2. 前端接入引入文件、初始化与工具栏裁剪2.1 先看懂解压后的目录结构拿到 ueditor1.4.3.zip解压之后第一眼会懵目录不少但别慌真正要关心的就几个。根目录下的ueditor.config.js是全局配置文件ueditor.all.js是完整编辑器代码ueditor.all.min.js是压缩版。lang/目录里是中文和英文语言包themes/是皮肤样式third-party/里是视频上传、代码高亮这些第三方插件。前端引入时有个常见误区以为引入ueditor.min.js就行结果页面报UEDITOR_CONFIG is not defined。原因在于ueditor.all.js里包含了配置逻辑而压缩版的加载顺序有依赖。我一般直接引ueditor.all.js上线前再换成ueditor.all.min.js省去排查顺序问题的麻烦。!-- 依赖 jQuery先引入 -- script src/static/js/jquery.min.js/script !-- UEditor 核心文件 -- script src/static/ueditor/ueditor.config.js/script script src/static/ueditor/ueditor.all.js/script !-- 语言包 -- script src/static/ueditor/lang/zh-cn/zh-cn.js/script逻辑说明ueditor.config.js会定义全局变量UEDITOR_CONFIGueditor.all.js读取这个变量来完成初始化所以顺序不能反。lang/zh-cn/zh-cn.js负责把编辑器内部按钮、提示文字替换成中文不引入的话默认英文界面。jQuery 是 UEditor 的依赖项这个老版本不能免 jQuery 运行。参数说明如果你用的是 1.4.3 之后的分支有些版本支持去掉 jQuery。但 1.4.3 这个版本我建议保留去掉之后视频上传和对话框拖拽会出现兼容问题没必要省这个体积。2.2 用 getEditor 做基础初始化页面里放一个script typetext/plain标签注意这个标签类型不能是text/javascriptUEditor 靠这个类型来识别容器。div ideditorContainer script typetext/plain idmyEditor 这里是默认内容支持 HTML 格式 /script /div然后在你自己的 JS 里调用var ue UE.getEditor(myEditor, { initialFrameWidth: 100%, initialFrameHeight: 400, autoHeightEnabled: false });逻辑说明UE.getEditor会先查询这个 ID 是否已经有实例如果有就返回现有实例没有才创建。这也是多实例场景的基础。initialFrameWidth设为100%可以让编辑器宽度自适应容器否则默认是 1000 像素固定宽度在窄屏布局里很容易溢出。autoHeightEnabled关闭后编辑器高度固定为 400避免内容一变多就把页面撑得忽长忽短。参数说明autoHeightEnabled默认是true但配合initialFrameHeight使用时行为会有点怪——编辑区高度会随内容自动增长布局会跳动。我一般做后台表单时直接关掉用initialFrameHeight定死高度内容多了内部滚动。2.3 工具栏裁剪只留业务需要的按钮默认工具栏会展示全部按钮从撤销重做到字体颜色到视频上传一大排很多按钮业务里根本用不到。截图的、打印的、搜索替换的放在后台编辑器里反而让用户困惑。var ue UE.getEditor(myEditor, { toolbars: [[ fullscreen, source, undo, redo, bold, italic, underline, fontborder, strikethrough, removeformat, formatmatch, autotypeset, blockquote, pasteplain, forecolor, backcolor, insertorderedlist, insertunorderedlist, selectall, cleardoc, rowspacingtop, rowspacingbottom, lineheight, customstyle, paragraph, fontfamily, fontsize, justifyleft, justifycenter, justifyright, justifyjustify, link, unlink, insertimage, insertvideo, attachment, insertcode, horizontal, date, time, spechars ]], toolbars: [[ fullscreen, source, undo, redo, bold, italic, forecolor, backcolor, insertorderedlist, insertunorderedlist, justifyleft, justifycenter, justifyright, link, unlink, insertimage, insertvideo, attachment, insertcode ]] });逻辑说明toolbars是一个二维数组外层数组的每个元素代表一行内层数组是该行的按钮列表。上面这段我故意写了两遍意思是你可以对比——第一份是保留较多的配置第二份是裁剪后的精简配置实际使用里只保留一份即可。精简版去掉了fullscreen之外的多数重按钮保留了排版、列表、插入图片视频和附件这些后台最常用的功能。参数说明按钮名是 UEditor 内部固定的字符串写错不会报错但按钮不显示。比如insertimage如果拼成insertImage静默失效排查时只能逐个对照官方按钮列表。建议每次裁剪完都刷新页面点一遍保留的按钮确认没有缺。2.4 多实例与实例销毁后台常有“一个页面放多个编辑器”的场景比如一个商品详情页有多段富文本。直接对每个容器都调用一次UE.getEditor是可以的但要注意相同 ID 的容器不行每个编辑器容器 ID 必须唯一。// 初始化 var ue1 UE.getEditor(editor1); var ue2 UE.getEditor(editor2); // 页面关闭或切换选项卡时销毁 if (ue1) { ue1.destroy(); } if (ue2) { ue2.destroy(); }逻辑说明getEditor和createEditor的区别就在这——getEditor是获取已有实例没有才创建createEditor是强制新建哪怕已存在同 ID 实例也会重复创建导致内存泄漏。我在某个后台系统里踩过一次用createEditor动态创建了 3 次同 ID 编辑器页面越来越卡控制台报editor is already exists。后来统一改成getEditor并在弹窗关闭时调用destroy()问题消失。参数说明destroy()会释放编辑器绑定的 DOM 事件和内部对象但现在getEditor这个实例变量本身还引用着置空即可彻底释放。3. 服务端接入PHP 上传接口与返回值格式3.1 controller.php 是服务端入口UEditor 1.4.3 的 PHP 版服务端代码在php/目录下核心入口是controller.php。它接收一个action参数来区分请求类型常见的是config、uploadimage、uploadvideo、uploadfile、listimage。// php/controller.php 简化后的处理逻辑 ?php header(Content-Type: text/html; charsetutf-8); $action $_GET[action]; switch ($action) { case config: $result json_decode(preg_replace(/\/\*[\s\S]*?\*\//, , file_get_contents(config.json)), true); break; case uploadimage: $result include(action_upload.php); break; case listimage: $result include(action_list.php); break; default: $result json_encode(array(state 请求地址出错)); break; } echo $result;逻辑说明控制器拿到action后分派到不同处理文件。config动作做的是把config.json读取出来里面注释会被正则去掉再解析成 JSON。uploadimage走action_upload.php执行真正的文件保存逻辑。整个链路的配置中心就是config.json前端初始化时会先请求一次config拿到上传路径、大小限制、类型白名单。参数说明如果你把controller.php放到子目录或者对入口做了权限控制前端serverUrl必须对应修改。这个参数在前端ueditor.config.js里配置默认是php/controller.php?actionconfig。很多集成失败都出在这一步——前端文件路径对但serverUrl指向了错误位置。3.2 config.json 里的关键参数config.json基本决定了上传行为。文件里大多是中文字段可读性不错但有几个参数经常被忽略{ imageActionName: uploadimage, imageFieldName: upfile, imageMaxSize: 2048000, imageAllowFiles: [.png, .jpg, .jpeg, .gif, .bmp], imageCompressEnable: true, imageCompressBorder: 1600, imageInsertAlign: none, imageUrlPrefix: , imagePathFormat: /upload/image/{yyyy}{mm}{dd}/{time}{rand:6} }逻辑说明imageFieldName是上传时的表单字段名前端传upfilePHP 端用$_FILES[upfile]接收这个名不能乱改否则两端对不上。imageMaxSize单位是字节2048000 就是 2MB。imagePathFormat决定文件存储路径花括号里是动态变量——{yyyy}{mm}{dd}是年月日{time}是时间戳{rand:6}是 6 位随机数。按日期分目录是惯例避免单目录文件过多。参数说明imageCompressEnable开启后超过imageCompressBorder宽度的图片会被等比压缩目的是控制体积。但有个坑这个压缩会把 PNG 的透明度丢掉如果你业务里需要透明图必须把imageCompressEnable设为false不然用户上传的 PNG 变成黑底。3.3 上传返回格式前端按字段名认数据上传是否成功前端不看 HTTP 状态码而是看返回 JSON 里的state字段。这是 UEditor 约定好的格式改不得。{ state: SUCCESS, url: /upload/image/20250115/123456_789012.jpg, title: 123456_789012.jpg, original: 用户上传时显示的文件名.jpg, type: .jpg, size: 204800 }逻辑说明state必须是大写SUCCESS小写success都不行前端判断是 SUCCESS。url是文件访问路径如果是相对路径前端会直接拼在编辑器当前域名下如果配置了 CDN这里返回完整 URL 也可以。original是用户在本地看到的文件名用于回显提示可以带中文但要确保接口输出 UTF-8。title存在服务器上的文件名。这五个字段缺一不可少一个图片就显示不出来。参数说明有些开发者会把state写成ok或true前端一直拿不到成功状态表现为图片上传后就地消失。这是我自己集成时翻车最多次的地方——原装action_upload.php不会出这个问题但凡你手写了上传接口第一件事就是核对这个字段。3.4 常见的服务端改造点原装action_upload.php能用但生产环境基本都要改。我遇到过三种高频需求上传到 OSS 而不是本地磁盘、按登录用户分目录、限制文件类型。前两种要改action_upload.php里的保存逻辑第三种直接改config.json。// action_upload.php 中保存文件的核心段 $file $_FILES[$fieldName]; $uploadFilePath $uploadDir . $uploadFileName; // 原逻辑是 move_uploaded_file改造后改为上传到对象存储 $result $ossClient-putObject($bucket, $objectKey, file_get_contents($file[tmp_name])); if ($result) { $response array( state SUCCESS, url $cdnHost . / . $objectKey, title $uploadFileName, original $file[name], type . . $fileExt, size $file[size] ); }逻辑说明改成对象存储后url要返回 CDN 完整地址。move_uploaded_file不再使用而是读取临时文件内容直接上传。这个改动不影响前端协议前端拿到url就能回显。要注意的一点是对象存储的读写权限、bucket 名称这些要提前配好否则putObject返回的异常信息会被吞掉前端永远只看到state: 上传失败排查半天才发现是密钥过期。参数说明文件类型白名单建议在服务端再校验一遍config.json管的是前端提示真正防止用户传.php、.exe还得靠服务端拦截。我一般会同时检查扩展名和 MIME 类型两者都通过才放行。4. 存储与安全正文取值、XSS 过滤与图片路径4.1 从编辑器拿内容getContent 和 getContentTxt表单提交时编辑器内容是一个隐藏字段。UEditor 提供getContent()返回完整 HTMLgetContentTxt()返回去 HTML 的纯文本。后台存 HTML 是必要的否则用户排版信息全丢。input typehidden namecontent idcontentValue button idsubmitBtn提交/button$(#submitBtn).on(click, function () { var content ue.getContent(); $(#contentValue).val(content); // 提交表单 });逻辑说明getContent()拿到的是富文本 HTML里面包含图片标签、段落标签、样式。存入数据库后下次读取出来直接放到编辑器的getContent容器里就能回显。需要注意如果内容是从数据库读出来再塞回编辑器的用ue.setContent(html)而不是直接改容器内容前者会经过编辑器内部处理保证图片、视频能正常加载。参数说明getContentTxt()适合用来做摘要、搜索索引它会把p、br替换成换行符文本干净但图片信息全部丢失。这两个方法各有用处别混用。4.2 服务端入库前的 XSS 过滤1.4.3 这个版本有个历史遗留问题前端过滤并不严格word粘贴进来的带样式内容还可能包含危险脚本。我处理这类后台内容时的习惯是入库前统一过滤而不是信任编辑器输出。H3 4.2.1 用 HTMLPurifier 做白名单过滤// composer require ezyang/htmlpurifier require_once /vendor/autoload.php; $config HTMLPurifier_Config::createDefault(); $config-set(HTML.Allowed, p,br,strong,em,u,ol,ul,li,a[href|title|target],img[src|alt|width|height],blockquote,pre,code,span[style],font[color|size]); $config-set(HTML.AllowedAttributes, a.href,a.title,a.target,img.src,img.alt,img.width,img.height,span.style,font.color,font.size); $config-set(Attr.AllowedFrameTargets, array(_blank, _self)); $purifier new HTMLPurifier($config); $cleanHtml $purifier-purify($_POST[content]);逻辑说明HTMLPurifier 按白名单过滤不在列表里的标签和属性会被删掉script、onclick、javascript:这类危险内容直接清除。这段配置保留了编辑器常用的段落、图文、链接、列表、代码块标签。HTML.Allowed控制标签HTML.AllowedAttributes控制每个标签能带的属性span[style]保留字体颜色和背景色。Attr.AllowedFrameTargets让target_blank能正常输出。参数说明如果你不需要用户填颜色span[style]可以直接去掉减少被注入style样式的面。另外注意font[color|size]是旧式标签HTMLPurifier 默认不支持需要额外配置如果不想折腾就直接删掉这个标签让用户只用工具栏里预设的样式。H3 4.2.2 入库时统一走过滤函数function filterEditorContent($content) { static $purifier null; if ($purifier null) { $config HTMLPurifier_Config::createDefault(); // 具体配置略与上面一致 $purifier new HTMLPurifier($config); } return $purifier-purify($content); } $safeContent filterEditorContent($_POST[content]); // 再存入数据库 $stmt-execute(array(:content $safeContent));逻辑说明把过滤器封装成函数所有接收编辑器内容的入口都调它一遍。静态变量保证整个请求周期只初始化一次HTMLPurifier实例因为它的配置加载比较重每次都 new 一个会拖慢请求。入库前过滤有两个好处一是数据源头干净二是即使哪天编辑器升级或换掉数据库里存量数据也是安全的。4.3 图片路径问题相对路径与域名迁移UEditor 存的图片地址默认是相对路径比如/upload/image/20250115/xxx.jpg。这在单机部署下没问题但一旦遇到下面两种情况就麻烦静态资源用独立域名、网站从 http 切 https。我处理过的一个实际案例某公司后台从 HTTP 切到 HTTPS结果历史文章里的图片全部加载不出来原因就是config.json里imageUrlPrefix是空的前端回显时拼了http://前缀浏览器直接拦截了混合内容。{ imageUrlPrefix: https://cdn.xxx.com, imagePathFormat: /upload/image/{yyyy}{mm}{dd}/{time}{rand:6} }逻辑说明imageUrlPrefix加上了 CDN 或静态域名的完整协议头前端拿到url后会拼成https://cdn.xxx.com/upload/image/20250115/xxx.jpg。这样切 https 之后新上传的图片不再出现混合内容拦截。历史数据里的相对路径怎么处理我一般写一个一次性脚本在数据库里把src/upload/替换成srchttps://cdn.xxx.com/upload/。这个操作要提前备份数据库因为替换范围是整个content字段误替换会很难回滚。参数说明imageUrlPrefix只在回显时生效存入数据库的 HTML 里其实是相对路径还是带前缀的绝对路径取决于编辑器的实际行为——上传成功返回的url是相对路径但getContent()输出时会把前缀拼上再塞进 HTML。所以这个配置影响的是最终存入数据库的值这点要心里有数。5. 常见问题排查五个高频坑的定位思路5.1 上传图片一直失败返回state: 上传失败现象点击插入图片选完文件后编辑器底部提示上传失败控制台看接口返回 JSONstate不是SUCCESS。原因最常见的是php/目录没有写入权限move_uploaded_file无法把临时文件移动到目标目录其次是config.json里定义的imagePathFormat生成的目录不存在PHP 不会自动创建。解决先给上传目录递归授权写权限。然后打开action_upload.php在移动文件之前加一行mkdir($uploadDir, 0777, true);确保目录存在。改完重试如果还是失败打开 PHP 错误显示直接看具体报错。5.2 图片上传成功但编辑器里不显示现象接口返回state: SUCCESS编辑器里却看不到图片刷新页面后图片地址是坏的。原因返回的url是相对路径但前端页面用了子目录路由比如后台地址是/admin/index.php编辑器把/upload/...解析成/admin/upload/...找不到文件。解决在ueditor.config.js里设置正确的serverUrl同时确保config.json的imageUrlPrefix不是空字符串。如果后台部署在子目录最好让url返回完整路径省去拼接歧义。我一般会让上传接口返回带域名的完整url毕竟从SUCCESS到显示不出来最隐蔽的原因就是路径拼接。5.3 修改 config.json 后不生效现象改了imageMaxSize、imageAllowFiles刷新页面重试行为没变化好像配置是写死的。原因controller.php里的config动作读文件时用了file_get_contents但 PHP 的opcache会缓存文件内容甚至浏览器端也可能缓存了actionconfig的响应。另外config.json里有中文注释JSON 解析失败时 PHP 会静默返回null前端拿不到配置就退回到代码里的默认值。解决在controller.php的config动作里拼接一个时间戳参数去缓存或者手动清掉opcache。我自己的习惯是改完config.json后浏览器里直接访问php/controller.php?actionconfig看返回的 JSON 里是不是新值先用这个动作确认服务端确实读了新配置。5.4 多实例时编辑器互相干扰现象页面里放了两个编辑器在第一个里输入文字第二个的工具栏状态也跟着变甚至内容串了。原因UE.getEditor在全局注册了实例列表但如果两个容器的 ID 有相同前缀或者代码里用了UE.getEditor(editor)这种写法而后面的实例 ID 是editor2getEditor匹配到第一个实例就会复用。解决每个编辑器容器 ID 必须全局唯一初始化后保存返回值到独立变量。不要依赖UE.getEditor再去拿实例直接用保存好的变量操作。我在做某跨平台系统的后台时遇到过动态生成的弹窗里重复初始化同 ID 容器改成每次弹窗打开时先destroy再getEditor就好了。5.5 前后端分离后上传报跨域错误现象前端部署在app.xxx.com服务端接口在api.xxx.com上传图片直接报跨域浏览器拦截了请求。原因上传接口和前端域名不一致CORS 没有配置。UEditor 的请求是普通表单提交需要服务端在响应头部加上Access-Control-Allow-Origin。解决在controller.php入口处统一设置 CORS 头。header(Access-Control-Allow-Origin: *); header(Access-Control-Allow-Methods: POST, GET, OPTIONS); header(Access-Control-Allow-Headers: Content-Type, Authorization);逻辑说明这三行放到controller.php最顶部所有接口响应都会带上跨域头。*表示任意域名都能访问如果你对安全有要求可以把*换成具体的前端域名。注意OPTIONS预检请求要单独处理否则浏览器会在正式上传前拦住。6. 调试与验证我每次集成后必走的一遍自检脚本集成完 UEditor 后我不会急着点界面而是先跑一遍接口级验证。这个习惯帮我省了很多“看起来能显示但一提交就完蛋”的夜班。做法很简单用 PHP 写一个命令行脚本按顺序验证上传、回显、过滤、存储、再渲染这五个环节。?php // verify_ueditor.php $baseUrl http://localhost/ueditor/php/controller.php; // 1. 验证 config 接口 $configJson file_get_contents($baseUrl . ?actionconfig); $config json_decode($configJson, true); if ($config null) { exit([FAIL] config 接口返回不是合法 JSON\n); } echo [OK] config 接口正常imageMaxSize . $config[imageMaxSize] . \n; // 2. 验证上传接口用 curl 模拟表单上传 $ch curl_init($baseUrl . ?actionuploadimage); $postData array( upfile new CURLFile(realpath(./test.png), image/png, test.png) ); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response curl_exec($ch); curl_close($ch); $uploadResult json_decode($response, true); if ($uploadResult[state] ! SUCCESS) { exit([FAIL] 上传接口异常: . $response . \n); } echo [OK] 上传成功url . $uploadResult[url] . \n; // 3. 验证 URL 可访问 $imageContent file_get_contents($uploadResult[url]); if ($imageContent false) { exit([FAIL] 上传后的图片无法通过 URL 访问\n); } echo [OK] 图片 URL 可访问大小 . strlen($imageContent) . 字节\n;这段脚本三个作用第一确认config接口输出合法 JSON第二真实模拟一次文件上传第三验证上传后的文件能通过 URL 访问。跑完这三步集成就算完成了一半。之后再到页面里操作编辑器填入一段带script的内容提交后检查数据库里存的是不是过滤后的安全 HTML。这也是我给每个接入过 UEditor 的项目都会留的一份边界测试记录。参数说明CURLFile是 PHP 5.5 以上处理文件上传的标准方式旧的写法在更高版本 PHP 里已经废了。test.png得先准备在脚本同级目录。curl_exec之后务必检查curl_error接口不通时这里会直接报网络原因。从那以后我每次给后台接入或升级编辑器配置都强制走一遍这个脚本上传、回显、过滤三件事没有一次是通过“感觉没问题”蒙混过关的。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑