# 保哥笔记 — 网站建设 > 本分片含 3 篇文章,按发布日期倒序。全部分片索引见 https://zhangwenbao.com/llms-full.md **站点**:https://zhangwenbao.com/ **分类**:网站建设 **生成**:2026-06-04 23:09:29 CST --- ## KindEditor批量上传图片限制:2方法+避坑实战 - URL:https://zhangwenbao.com/kindeditor-images-upload-batches-modified.html - 分类:网站建设 - 发布:2020-11-20 | 更新:2026-06-02 - 摘要:KindEditor批量上传图片数量与大小限制如何修改?详解imageSizeLimit、imageUploadLimit两种放宽方案、kindeditor-all.js源码改法、PHP与Nginx配套配置、升级合并脚本和性能优化技巧。 - 关键词:kindeditor,富文本编辑器,JavaScript,文件上传,网站开发 > **TLDR**:摘要:KindEditor批量上传图片的数量和大小限制怎么改?本文先讲清两个限制到底从哪来,给两种放宽方案——初始化时直接配imageSizeLimit和imageUploadLimit参数、直接改kindeditor-all.js源码默认值,再讲服务端PHP与接口必改的参数、升级时怎么保留改动、大批量上传的性能调优和迁移建议。 > 摘要:KindEditor批量上传图片的数量和大小限制怎么改?本文先讲清两个限制到底从哪来,给两种放宽方案——初始化时直接配imageSizeLimit和imageUploadLimit参数、直接改kindeditor-all.js源码默认值,再讲服务端PHP与接口必改的参数、升级时怎么保留改动、大批量上传的性能调优和迁移建议。 保哥前段时间帮朋友维护一个老的企业站,后台编辑器用的是KindEditor (https://zhangwenbao.com/ecshop-replaces-baidu-editor-to-add-batch-upload-pictures.html) 4.1系列。客户那边经常一次要往一篇文章里塞五六十张产品图,结果一上传就提示一次最多20张、单图不超过1MB。这两个限制其实是KindEditor自己默认的,并不是PHP或者Nginx卡住的,所以解决思路也比较固定。下面这篇是我把改这台机器的过程整理出来的实战记录,包含初始化参数配置、源码层修改、PHP与Nginx的配套参数、升级时的合并脚本和大批量上传的性能调优经验,方便后面再有项目踩到同一个坑可以直接复用,省掉每次重新摸一遍的时间成本。 ## 先确认两个限制到底是从哪里来的 这是排查这类问题最重要的一步,否则改了半天发现改错地方,白白浪费时间。KindEditor的批量图片上传插件叫multiimage,它的两个默认值写在kindeditor-all.js里,在4.1.11版本中大约在8085行附近: imgPath = self.pluginsPath + 'multiimage/images/', imageSizeLimit = K.undef(self.imageSizeLimit, '1MB'), imageFileTypes = K.undef(self.imageFileTypes, '*.jpg;*.gif;*.png'), imageUploadLimit = K.undef(self.imageUploadLimit, 20), 这里K.undef的意思是:如果调用方没有显式传入imageSizeLimit和imageUploadLimit,就用默认的1MB和20。换句话说,限制有两个层次。第一层是前端默认值,写死在kindeditor-all.js里,影响所有调用方;第二层是后端兜底,上传接口(PHP的upload_json.php或者你自己实现的接口)会再校验一次。两个层次都要看,前端只是不让你选超过限制的文件,真正能不能存下来还得看后端。 我之前调试时就遇到过前端配好了10MB,后端php.ini还是2M,结果换成弹服务器拒绝上传,那种报错更迷惑。所以正确的姿势是先想清楚自己改的到底是哪一层,避免来回折腾。三层校验从浏览器到磁盘的顺序大致是:浏览器JS校验文件大小和数量、上传请求经过Nginx判断client_max_body_size、PHP接管读取upload_max_filesize和post_max_size、最后KindEditor自带upload_json.php或自写接口再做MIME和扩展名校验。任何一层没改对,上传都会失败,错误现象还各不相同。 ## 三层校验的失败现象与定位方法 建立一张速查表,遇到失败时直接对照定位,能节省大量盲改时间: 失败现象 | HTTP状态 | 失败层次 | 定位思路 | 前端弹窗"文件大小超过1MB" | 未发起请求 | JS默认值 | 改imageSizeLimit或源码K.undef默认值 | 前端弹窗"最多上传20个文件" | 未发起请求 | JS默认值 | 改imageUploadLimit或源码K.undef默认值 | 上传后浏览器报413 Request Entity Too Large | 413 | Nginx | 放大client_max_body_size | 上传后浏览器报500 Internal Server Error | 500 | PHP内存或体积 | 检查memory_limit和upload_max_filesize | 页面无响应,$_FILES为空 | 200但空 | PHP整体POST | 放大post_max_size、max_file_uploads | 返回JSON里写着error: 上传文件大小超过限制 | 200 | upload_json.php | 改max_size字节数 | 返回JSON里写着error: 不允许的文件类型 | 200 | upload_json.php白名单 | 改$ext_arr的image数组 | 这张表是我过去5年维护KindEditor项目时反复用到的,按HTTP状态码倒推根因,比一遍遍把所有配置都改一遍要科学得多。第一次遇到413时我花了2小时才发现是Nginx卡的,后来直接看状态码就知道改哪里。 ## 方法一:在初始化KindEditor时直接配置参数 这是我最推荐的做法,因为不用改第三方源码,未来升级KindEditor的时候不会被覆盖丢失,对版本管理也最友好。把页面里调用KindEditor的那段JS改成下面这样: 几个我自己踩过的小坑顺手说一下。第一,imageSizeLimit一定要带单位,写成数字会按字节算,写成10直接变成10字节,相当于完全不让上传。第二,imageUploadLimit是数字不是字符串,这个参数没单位,写成字符串100也能跑但和官方文档不一致,建议保持数字类型避免类型校验误判。第三,旧浏览器(比如IE9)会忽略multiple属性,自然也就没有批量概念,遇到这种情况只能升级浏览器,没法靠KindEditor解决。第四,如果你给页面同时挂了多个编辑器实例,每个实例的参数要单独传,不然第二个会继承第一个的默认配置导致行为诡异。 第五个坑相对隐蔽:当你在动态加载的iframe或Vue组件里初始化KindEditor时,KindEditor.ready可能因为DOM未就绪而提前执行,导致参数丢失。解决办法是在容器节点渲染完成后再手动调用KindEditor.create,或者用nextTick等待DOM刷新。这个问题在用Element UI的Dialog和Modal时特别容易出现,我有一次为此调试了整整一天,最后发现是Dialog的v-if销毁了DOM节点。 ## 方法二:直接改kindeditor-all.js源码默认值 如果项目里有几十个页面都在初始化KindEditor,又不想一个一个改JS,可以选择改源码默认值。先把原始文件备份一份,再用编辑器搜索multiimage,定位到上面那段K.undef赋值代码,把后两行改成: imageSizeLimit = K.undef(self.imageSizeLimit, '10MB'), imageUploadLimit = K.undef(self.imageUploadLimit, 100), 注意改完别忘了清缓存。如果你的网站套了CDN,最稳妥的做法是给kindeditor-all.js加一个版本号查询串: 这样CDN和浏览器都会拉新版本。我自己写过一个小脚本,把每次发布的时间戳自动写进script src里,省掉手动改版本号的麻烦。如果项目里有自动构建工具,比如Webpack或Vite,可以让构建时自动注入哈希;如果没有,写个简单的PHP帮助函数也够用: function asset($path) { $abs = __DIR__ . '/public' . $path; $ver = file_exists($abs) ? filemtime($abs) : time(); return $path . '?v=' . $ver; } 这样模板里写asset函数包裹路径就会自动带上文件最后修改时间,文件改一次缓存就被打破一次,绝对不会出现客户那边看到的还是旧版的尴尬。在大型项目里我还会进一步做版本号的子资源完整性校验(SRI),给script标签加integrity属性,避免CDN劫持后用户拉到被篡改的JS文件。 ## 服务端PHP配套必改的参数 这一步绝对不能忘。前端放宽了,服务端没放宽,照样上传失败,而且报错还不直观。以php.ini为例,至少要确认下面几个值: file_uploads = On upload_max_filesize = 20M post_max_size = 220M memory_limit = 256M max_file_uploads = 200 max_execution_time = 120 max_input_time = 120 这里有个隐藏规则:post_max_size必须大于等于单图体积乘以一次上传的张数,否则PHP会直接吃掉整个POST而$_POST、$_FILES都是空的,前端表现就是按了上传没反应,连PHP错误日志里都看不到记录,非常容易把人逼疯。我的经验值是:单图10MB+张数100=1000MB理论上限,实际配置220MB通常足够覆盖典型批量上传场景,因为客户极少一次性满额上传。 如果用的是Nginx,记得在站点配置里把上传体积也放开: client_max_body_size 220m; client_body_buffer_size 1m; client_body_timeout 120s; send_timeout 120s; 配置改完执行nginx -t检查语法,确认无误后reload nginx,然后reload php-fpm让两边都生效。重启与reload的区别在于:reload不会断开现有连接,对生产环境更友好;restart会强制断开,建议只在调试时使用。我自己排查这类问题的顺序是:先开浏览器F12看上传失败的请求HTTP状态码,413一定是Nginx拒绝,500一般是PHP内存或upload_max_filesize不够,200但返回的JSON里写着error才是KindEditor自己的逻辑校验。按状态码倒推根因,比把所有配置都改一遍要科学得多。 ## 服务端接口本身的限制也要看一眼 第三层兜底是KindEditor自带的php/upload_json.php,里面也写死了一份白名单和大小限制。打开这个文件,会看到类似下面的代码: $ext_arr = array( 'image' => array('gif', 'jpg', 'jpeg', 'png', 'bmp'), 'flash' => array('swf', 'flv'), 'media' => array('swf', 'flv', 'mp3', 'wav', 'wma', 'wmv', 'mid', 'avi', 'mpg', 'asf', 'rm', 'rmvb'), 'file' => array('doc', 'docx', 'xls', 'xlsx', 'ppt', 'htm', 'html', 'txt', 'zip', 'rar', 'gz', 'bz2'), ); $max_size = 1000000; $max_size是字节数,1000000大约就是1MB。要改成10MB直接写: $max_size = 10 * 1024 * 1024; 如果你想顺便支持webp,把image那一行改成包含webp扩展名的数组,再把avif也加上,能覆盖2025年主流手机厂商导出的新一代图片格式。这一步是不少教程会漏掉的地方,前端、php.ini、Nginx都改完了还失败,往往就是这里在卡。还有一个小细节:upload_json.php默认会用getimagesize来确认是不是真图片,遇到某些手机厂商导出的特殊webp时这个函数会失败,建议改成结合mime_content_type和扩展名一起判断,可以兼容更多格式。 从安全角度看,upload_json.php还有一个经典漏洞:默认会用原始文件名拼接路径保存,如果用户上传名为../../../../etc/passwd.jpg的文件,再加上MIME校验不严,可能导致目录穿越。修复办法是用md5(uniqid())重命名所有上传文件,扩展名也用白名单严格校验,永远不要信任客户端传上来的文件名。 ## 升级版本时如何保留这些改动 KindEditor已经多年没有大版本更新,但偶尔还是会打安全补丁。每次升级我都会经历同样的流程,干脆固化成一个清单放在项目README里:备份现有的kindeditor整个目录、解压新版到临时目录、用diff对比新旧kindeditor-all.js中multiimage段的差异、把自定义的imageSizeLimit和imageUploadLimit重新合并进去、把php/upload_json.php里的max_size和ext_arr同样合并、把旧目录改名做备份再把新版重命名为正式目录、最后给JS加一个新版本号防CDN缓存。 我自己还多写了一个简单的shell脚本,用来一次性完成上面这些事: #!/usr/bin/env bash set -euo pipefail ROOT=/var/www/html/static/kindeditor DATE=$(date +%Y%m%d%H%M) cp -a "$ROOT" "$ROOT.bak.$DATE" unzip -o /tmp/kindeditor-new.zip -d /tmp/ke-new rsync -a /tmp/ke-new/kindeditor/ "$ROOT/" sed -i "s/'1MB'/'10MB'/" "$ROOT/kindeditor-all.js" sed -i "s/imageUploadLimit, 20/imageUploadLimit, 100/" "$ROOT/kindeditor-all.js" echo "done at $DATE" 这套流程我现在用了三年,一次都没翻过车。比起每次升级都靠记忆去改,写下来照做实在轻松太多,新人接手也能立刻按部就班执行,不会一上手就把客户的配置覆盖丢。脚本里的关键点:set -euo pipefail能让任何一步失败立刻退出避免半成品状态、cp -a保留权限和时间戳、rsync -a保证目录权限正确、sed的两个替换精确锁定要改的行不会误伤其他位置。 ## 大批量上传的真实性能调优经验 聊完了限制怎么改,再说点实际遇到的性能问题。客户那边经常一次拖几十张高分辨率手机照片进来,单张原图动辄五六兆,真要一次性丢上来很容易把页面卡到无响应。保哥总结过一些可以立竿见影的优化办法。 第一是在前端加上一个简单的图片预压缩流程。浏览器现在对Canvas和OffscreenCanvas支持都已经很完善,可以在用户选完图片之后立刻在客户端按最大边缩到1920像素,再丢给上传接口。这样既能减少流量又能减少服务器内存压力。50张原图平均5MB的总量250MB,压缩后能降到80至100MB,上传时间从原来的2分多钟缩短到30秒左右,体验完全不在一个量级。 第二是在服务器端用imagick替代gd处理大图。imagick对内存的控制远比gd友好,处理一张二十兆的RAW时不会动不动就把PHP进程撑爆。安装命令是apt install php8.2-imagick或yum install php-imagick,重启php-fpm生效。imagick还支持读取RAW、HEIC等手机厂商专有格式,对苹果设备导出的图片兼容性最好。 第三是把图片的原图存到对象存储而不是本机磁盘。常见选型包括阿里云OSS、腾讯云COS、AWS S3、Cloudflare R2等。R2在2025年起免出口流量费用,对流量大的项目特别友好。原图存对象存储后接CDN,访问体验会有一个数量级的提升,源站只需要保留缩略图 (https://zhangwenbao.com/deformable-clipping-method-for-dedecms-thumbnails.html)本地缓存即可。 再说一个容易忽视的细节。KindEditor的批量上传走的是一个个独立的POST请求,每张图片对应一次HTTP调用,并不是把所有文件打包成一个请求一次传完。所以如果你只是放大了post_max_size而没有改max_file_uploads,看上去也还是能用,但实际上每张图片都在串行发起请求,浏览器一次只跑两到六个并行连接。要让批量上传感觉更快,可以在afterCreate里把上传插件的并发数手动调一下,比如把请求分成几组并发发出,再统一回写到编辑器里。改完之后客户那边明显感受到差别,原来上传50张图要等两分多钟,现在大概30秒就能搞定。 最后顺手提醒一句:所有客户端层面的优化都不能替代服务端校验,上传体积、文件类型、文件名安全性这些校验在后端必须都要做一遍,否则等于在前端贴了一张纸,恶意用户绕过去毫无成本。 ## 2026年KindEditor的生存现状与迁移建议 KindEditor的最后一次正式更新是2018年,2026年的今天它依然在数万个传统企业站、政府门户和论坛系统里跑得稳稳的。这种"老而弥坚"的现象其实非常合理:富文本编辑器的核心需求是稳定输出HTML、支持图片上传、能扩展自定义按钮,KindEditor这三件事都做得很扎实,新版编辑器拼的是协同编辑、Markdown、AI辅助等附加能力,对传统企业站不是刚需。 但如果你的项目有下面这些特征,可以考虑迁移到更现代的编辑器: - 用了TypeScript做技术栈,需要编辑器原生支持类型定义。 - 需要协同编辑功能,多人同时改一篇文章不冲突。 - 需要Markdown和富文本混排,方便开发者和编辑共用。 - 需要移动端友好,KindEditor在iOS Safari上的工具栏体验较差。 - 合规要求严格,新编辑器的XSS防护机制更完善。 主流替代方案的优劣对比: 编辑器 | 核心优势 | 主要劣势 | 迁移难度 | TinyMCE 7 | 商业支持、插件生态丰富 | 免费版功能受限、商用需授权 | 低 | CKEditor 5 | 协同编辑、Markdown混排 | API完全重写、迁移成本高 | 高 | wangEditor 5 | 国产、文档中文、轻量级 | 插件生态小、企业支持弱 | 中 | Quill 2 | 开源免费、Delta格式可控 | 富文本能力相对弱 | 中 | ProseMirror | 底层框架、自定义能力最强 | 需自行实现UI、开发周期长 | 高 | 我自己迁移过一次,做法是先写一个临时脚本把所有pre.wp-block-code这类老的内联样式批量改成新编辑器认识的标签,再迁移内容,迁移完肉眼抽检几篇典型文章,没问题再切流量,整个过程相对平稳。如果你的内容量超过万篇,建议分批迁移:先迁100篇试点,观察一周用户反馈,再批量迁剩余内容,避免一次性全量切换风险过大。 ## 常见问题解答 ## KindEditor默认能批量上传多少张图片? KindEditor的multiimage插件默认值写死在kindeditor-all.js里,一次最多20张、单张最大1MB。这两个值由K.undef(self.imageUploadLimit, 20)和K.undef(self.imageSizeLimit, 1MB)控制,没有显式传参时就走默认。要放宽必须从前端参数、源码默认值、服务端接口三层任选其一改起,单层改动不够会被另一层兜底卡住。一般推荐改前端参数加服务端配套,源码默认值改动留作所有页面统一升级时使用。 ## imageSizeLimit和imageUploadLimit参数怎么用? imageSizeLimit是单文件大小上限,必须带单位写10MB或20MB,写成纯数字会被当字节算导致几乎不能上传。imageUploadLimit是批量数量上限,纯数字传入即可,例如100或200。两个参数在KindEditor.create的配置对象里直接写,优先级高于源码默认值,是最干净的改法,未来升级KindEditor也不会丢失。配置完成后必须配合服务端php.ini和Nginx对应放大才能完整生效。 ## 改完前端后还是上传失败是什么原因? 最常见的是浏览器或CDN缓存了旧的kindeditor-all.js,给script标签加版本号查询串如?v=20260513能强制刷新。第二常见的是PHP和Nginx的兜底没改,post_max_size必须大于单图体积乘以一次上传张数,client_max_body_size也要同步放大。第三是KindEditor自带的php/upload_json.php里写死的max_size=1000000仍在卡,需要改成10*1024*1024。按F12浏览器开发者工具看请求状态码能快速定位失败层级。 ## 修改kindeditor-all.js源码升级时怎么保留? 用版本控制把diff保存下来。每次升级执行三步:备份现有目录、解压新版到临时位置、用sed把imageSizeLimit和imageUploadLimit两个默认值替换回自定义值。也可以写shell脚本固化流程,例如sed -i s/1MB/10MB/和sed -i s/imageUploadLimit, 20/imageUploadLimit, 100/。脚本配版本号查询串能彻底解决CDN缓存问题。建议把整个升级流程写进项目README,让新人接手也能照做。 ## 服务端PHP配套需要改哪些参数? php.ini里upload_max_filesize必须大于imageSizeLimit,post_max_size要大于单图乘以张数,max_file_uploads要大于imageUploadLimit数字,memory_limit建议256MB以上,max_execution_time和max_input_time给到120秒。Nginx的client_max_body_size、client_body_timeout、send_timeout同步放大。改完执行php-fpm reload和nginx reload两边生效,再开浏览器F12看上传请求状态码反向定位。 ## KindEditor 2026年还推荐使用吗? KindEditor已经多年没有大版本更新,但代码结构简单、性能稳定,至今仍是大量传统企业站和论坛的首选编辑器。如果项目需要更现代的编辑体验或团队需要TypeScript支持,推荐迁移到TinyMCE 7、CKEditor 5或国产的wangEditor 5。迁移前先用脚本把所有内联样式和老标签批量映射到新编辑器认识的格式,迁移后抽检几篇典型文章再切流量,能把迁移风险降到最低。 ## 上传大批量图片性能慢如何优化? 前端在用户选图后用Canvas或OffscreenCanvas按最大边1920像素预压缩,能减少70%以上的上传体积。服务端用imagick替代gd处理大图,imagick的内存控制比gd友好得多,处理20MB的RAW图也不会撑爆PHP进程。把原图存到对象存储而不是本机磁盘并接CDN边缘缩略图服务,能让首页响应快一个数量级。所有客户端优化必须配合服务端复检,文件类型、体积、文件名安全都要在后端再做一遍。 ## 结语 KindEditor虽然多年没有大版本更新,但它结构简单、改动透明,至今依然是很多老项目里最稳定的编辑器之一。只要你理解了它三层校验的逻辑——前端参数、源码默认值、服务端接口三处改动都能对得上号,这种限制类的问题基本就不会再困扰你了。如果项目计划长期维护,建议把本文的升级合并脚本和PHP/Nginx配套参数固化到部署文档里,新人接手也能按部就班执行;如果项目计划在未来一两年迁移到更现代的编辑器,可以先把本文方法一的KindEditor.create配置抽成独立模块,迁移时只需替换模块内部实现,外部调用代码不用动,把迁移成本压到最低。 ## 权威参考资料 ## KindEditor编辑器深度优化指南:上传目录归档、批量限额、p标签清理与CSRF安全加固 - URL:https://zhangwenbao.com/kindeditor-image-upload.html - 分类:网站建设 - 发布:2020-11-20 | 更新:2026-06-02 - 摘要:KindEditor开箱即用,但上传目录、批量限额、空标签和安全都得自己收口。本文给出具体代码:调批量上传数量与单图大小、按年月分目录归档并UUID重命名、清理空p与图片包段落,再补登录鉴权、双扩展名拦截、MIME真实类型校验和三层限额配置。 - 关键词:kindeditor,图片上传,上传目录,CSRF防护,富文本编辑器 > **TLDR**:摘要:KindEditor开箱即用,但上传目录、批量限额、空标签和安全都得自己收口。本文给具体代码——关闭网络图片标签页、调批量上传数量与单图大小、上传目录按日期归档并UUID重命名、清理图片自动包的p标签,再补CSRF与XSS与文件类型验证的安全加固,以及与WordPress和Discuz和DedeCMS的集成和实战故障排查。 > 摘要:KindEditor开箱即用,但上传目录、批量限额、空标签和安全都得自己收口。本文给具体代码——关闭网络图片标签页、调批量上传数量与单图大小、上传目录按日期归档并UUID重命名、清理图片自动包的p标签,再补CSRF与XSS与文件类型验证的安全加固,以及与WordPress和Discuz (https://zhangwenbao.com/discuz-global-variables-details.html)和DedeCMS的集成和实战故障排查。 KindEditor (https://zhangwenbao.com/kindeditor-images-upload-batches-modified.html) 是 2006 年起就被广泛集成进 PHP/.NET CMS 的所见即所得编辑器,DedeCMS、ECShop (https://zhangwenbao.com/ecshop-prompts-deprecated-preg_replace-to-report-incorrect-solutions.html)、phpcms、Discuz 多个版本都把它当过默认富文本编辑器。绝大多数项目用它都会撞上同一组痛点:图片上传目录混乱、上传大小受限、单图传完会自动包一层


、网络图片入口有安全隐患、多图批量上传数量太少。本文按上传目录、上传限额、网络图片关闭、p 标签包裹、视觉显示优化、CSRF 与 XSS 加固六个维度逐一给出代码改造,覆盖 KindEditor 4.1.x 主流版本。 ## KindEditor 整体架构与改造前的准备 ## 核心目录结构 典型部署路径下 KindEditor 的目录长这样: kindeditor/ ├── kindeditor-all.js # 完整版 JS(开发版) ├── kindeditor-all-min.js # 完整版 JS(压缩版) ├── kindeditor.js # 核心 JS ├── plugins/ # 插件目录 │ ├── image/ # 单图上传插件 │ ├── multiimage/ # 多图批量上传 │ ├── filemanager/ # 文件管理器 │ └── ... ├── themes/ # 皮肤 ├── lang/ # 多语言 ├── php/ │ ├── upload_json.php # 单图与文件上传后端 │ ├── file_manager_json.php │ └── JSON.php └── jsp/ # JSP 后端(如不用 JSP 可删) 改造前先做一份完整备份,所有路径写改动备忘以便回滚。 ## 识别 KindEditor 实例的两种调用形态 KindEditor 的初始化有两种常见写法,决定后续改造代码改哪儿: 形态 A:完整富文本编辑器 KindEditor.create('#editor', { uploadJson: '/kindeditor/php/upload_json.php', fileManagerJson: '/kindeditor/php/file_manager_json.php', allowFileManager: true, items: ['source','|','undo','redo','|','image','multiimage','...'] }); 形态 B:单独调用图片上传弹窗(不带编辑器) var editor = KindEditor.editor({ uploadJson: '/kindeditor/php/upload_json.php', allowFileManager: true }); editor.loadPlugin('image', function() { editor.plugin.imageDialog({ showRemote: false, clickFn: function(url, title, width, height) { // 处理结果 } }); }); ## 关闭网络图片标签页 KindEditor 默认的图片上传弹窗有“本地上传”“网络图片”“图片空间”三个标签页。“网络图片”让用户输入任意 URL 直接插入页面,对站点有两个隐患: - 外链 (https://zhangwenbao.com/free-backlink-building-strategies.html)图片随时失效,正文图片显示成裂图。 - 第三方图床的隐私追踪 cookie 会跟着请求发送,触发用户的隐私警告。 关闭方法按调用形态分两种: ## 形态 B(单独图片上传) 在调用 imageDialog 时直接传参: editor.plugin.imageDialog({ showRemote: false, showLocal: true }); ## 形态 A(编辑器内的图片按钮) 编辑 plugins/image/image.js,找到内部对 imageDialog 的调用: self.plugin.imageDialog({ imageUrl : K(this.cmd.range.startContainer).attr('src'), clickFn : function(url, title, width, height, border, align) { // ... } }); 加上 showRemote: false: self.plugin.imageDialog({ showRemote: false, imageUrl : K(this.cmd.range.startContainer).attr('src'), clickFn : function(url, title, width, height, border, align) { // ... } }); 同样的改法对 multiimage.js 也适用——多图批量上传弹窗也有网络图片入口。 ## 批量上传:调整数量、单张大小、并发 ## 前端限额 编辑 plugins/multiimage/multiimage.js,定位到: KindEditor.plugin('multiimage', function(K) { var self = this, imageSizeLimit = K.undef(self.imageSizeLimit, '5MB'), imageUploadLimit = K.undef(self.imageUploadLimit, 20); ... }); 把默认 5MB / 20 张分别改大。生产里我常用的设置是 30MB / 200 张: var imageSizeLimit = K.undef(self.imageSizeLimit, '30MB'), imageUploadLimit = K.undef(self.imageUploadLimit, 200); 不建议直接改成 1000 张,浏览器一次拖入 1000 张图片会让 DOM 卡死,分批上传体验更好。 ## 后端限额(upload_json.php) 编辑 php/upload_json.php,定位: $max_size = 1000 * 1024; // 默认 1MB 改成与前端一致: $max_size = 30 * 1024 * 1024; // 30MB ## php.ini 三个关键参数 仅改前后端不够,PHP 本身有三道闸门,必须同步调整: upload_max_filesize = 30M post_max_size = 100M max_file_uploads = 200 max_execution_time = 300 memory_limit = 256M post_max_size 必须 ≥ 单文件大小 × 单次上传数量,否则一次性上传 50 张 5MB 图会触发 413 Request Entity Too Large。memory_limit 不够会让 PHP 在 GD 库处理图片时内存不足致命错误。 ## nginx 同步调整 nginx 默认 client_max_body_size 是 1MB,必须改: http { client_max_body_size 100m; client_body_timeout 300s; client_body_buffer_size 128k; } 这一项不改的话,nginx 会先于 PHP 拒绝请求,前端看到的是 413 而不是 PHP 的友好错误提示。 ## Apache 同步调整 .htaccess 里加: php_value upload_max_filesize 30M php_value post_max_size 100M php_value max_execution_time 300 php_value memory_limit 256M LimitRequestBody 104857600 ## 上传目录改造与按日期归档 ## 原始默认行为 upload_json.php 默认所有文件都上传到 attached/ 目录,时间长了会累积几万个文件在一个目录里,运维查找麻烦、ext4 文件系统单目录文件数太多会导致 ls 卡顿、备份增量同步也变慢。 ## 按日期归档的改法 编辑 php/upload_json.php,定位: // 文件保存目录路径 $save_path = $php_path . '../attached/'; // 文件保存目录URL $save_url = $base_url . '../attached/'; 改成按年月分目录: $year_month = date('Ym'); // 例如 202405 $save_path = $php_path . '../attached/' . $year_month . '/'; $save_url = $base_url . '../attached/' . $year_month . '/'; if (!file_exists($save_path)) { mkdir($save_path, 0755, true); } 0755 在多数生产环境下足够(owner=apache/nginx 用户写入,group/other 只读),不要用 0777。 ## 按业务模块进一步细分 如果你的站点有多个业务模块(比如商品图、文章图、用户头像走同一个 KindEditor),可以通过 GET/POST 参数传递 module 名做更细分类: $module = isset($_REQUEST['module']) ? preg_replace('/[^a-z0-9_]/i', '', $_REQUEST['module']) : 'default'; $save_path = $php_path . '../attached/' . $module . '/' . $year_month . '/'; $save_url = $base_url . '../attached/' . $module . '/' . $year_month . '/'; preg_replace 这一步是安全屏障,避免恶意构造的 module 名做目录穿越(比如 module=../../etc)。 ## 文件名重命名规则 原始 upload_json.php 用以下规则给文件改名: $new_file_name = date('YmdHis') . '_' . rand(10000, 99999) . '.' . $file_ext; 这个规则在并发场景下还有概率撞名(同一秒内两个用户都摇到 12345)。建议改成 UUID 或加上原始文件名 hash: $new_file_name = date('YmdHis') . '_' . substr(md5(uniqid(mt_rand(), true)), 0, 12) . '.' . $file_ext; UUID 部分 12 位 hex 共 16^12 ≈ 281 万亿组合,撞名概率近乎为零。 ## 图片自动包 p 标签的处理 ## 问题描述 KindEditor 默认在图片插入到正文后会自动包一层


,原意是给用户留下后续输入文字的位置。但当用户连续插入多张图时,每张图前后都会出现一个空 p,正文渲染时多出大量空行,移动端尤其难看。 ## 方案 A:保留 p 但去掉空 p 编辑 kindeditor.js,找到这一段: html = html.replace(/(<(?:p|p\s[^>]*)>)\s*(<\/p>)/ig, '$1
$2'); 改成: html = html.replace(/(<(?:p|p\s[^>]*)>)\s*(<\/p>)/ig, ''); 原代码保留空 p 并填一个 br;新代码直接删除空 p。空段落消失,多图连排布局紧凑。 ## 方案 B:给所有图片单独包 p 这一改法适合“每张图一个段落”的版式(旅行游记、产品多图展示)。在 kindeditor.js 的 insertimage 函数里: insertimage : function() { var self = this; self.exec('insertimage', ...); } 改成把 img 包在 p 里再 exec: insertimage : function(url, title, width, height, border, align) { var img = '

' + title + '

'; this.exec('inserthtml', img); } ## 方案 C:清理后端正文 有些情况编辑器输出已经写库,再去前端清空 p 不现实。这时候改 PHP 后端,在保存前清理: function clean_editor_html($html) { // 删除空段落 $html = preg_replace('/]*>\s*()?\s*<\/p>/i', '', $html); // 合并多余空白 $html = preg_replace('/\s{2,}/', ' ', $html); return $html; } $content = clean_editor_html($_POST['content']); ## 视觉显示优化(编辑器内) ## 问题:富文本所见即所得不像 KindEditor 在 iframe 内渲染正文,自带 css 让段落紧贴、图片溢出、光标显示不清。这与最终前台展示效果差距大,作者不知道发出去会变成什么样。 ## 定位 _getInitHtml 函数 kindeditor.js 内 _getInitHtml 函数返回 iframe 内的初始 HTML,包含 head 里的 style 块。原始 style 大概长这样: 'body {' + ' font:12px/1.5 \'Microsoft YaHei\',\'simsun\',\'Helvetica\',sans-serif;' + ' margin:8px;' + '}' + 'p {margin:5px 0;}' + 'img {border:0;}' ## 对照前台样式调整 建议把 iframe 内的 css 与前台正文 css 大致对齐。我项目里常用的调整: 'body {' + ' font:16px/1.7 \'Microsoft YaHei\',\'PingFang SC\',sans-serif;' + ' margin:16px;' + ' color: #333;' + ' background: #fff;' + '}' + 'p {margin:12px 0; line-height:1.7;}' + 'p img {max-width:100%; height:auto; border-radius:4px;}' + 'img {border:0; padding:0 4px;}' + 'h2 {font-size:22px; margin-top:24px; border-left:4px solid #4a90e2; padding-left:10px;}' + 'h3 {font-size:18px; margin-top:18px; color:#4a90e2;}' + 'a {color:#4a90e2; text-decoration:none;}' + 'pre {background:#f4f4f4; padding:12px; border-radius:4px; overflow-x:auto;}' ## 安全加固:CSRF / XSS / 文件类型验证 ## upload_json.php 的默认安全漏洞 KindEditor 4.1.x 自带的 upload_json.php 有这些已知问题: - 没有 CSRF token 校验,任何域名都能往后端 post 文件。 - file_manager_json.php 没有权限校验,未登录用户能列出 attached/ 目录所有文件。 - 白名单扩展名靠后缀判断,能被双扩展名(pic.jpg.php)绕过。 - 未做 MIME 真实类型检查,能用伪造 header 上传 PHP 文件。 ## 加 CSRF token upload_json.php 顶部加: session_start(); if (empty($_REQUEST['_csrf']) || $_REQUEST['_csrf'] !== $_SESSION['kindeditor_csrf']) { header('Content-Type: application/json'); echo json_encode(['error' => 1, 'message' => 'CSRF check failed']); exit; } 前端调用 KindEditor 时把 token 传过去: KindEditor.create('#editor', { uploadJson: '/kindeditor/php/upload_json.php?_csrf=' }); ## 双扩展名拦截 原代码: $ext = strtolower(strrchr($file_name, '.')); $ext = trim($ext, '.'); if (!in_array($ext, $ext_arr[$dir_name])) { ... } 双扩展名(image.jpg.php)会被识别成 .php 拒绝,但 image.php.jpg 会被识别成 .jpg 通过——实际是 PHP 文件。修复:检查全部扩展名: $file_parts = explode('.', $file_name); $dangerous = ['php', 'phtml', 'php3', 'php4', 'php5', 'pht', 'asp', 'aspx', 'jsp', 'cer', 'cdx', 'asa']; foreach ($file_parts as $part) { if (in_array(strtolower($part), $dangerous)) { die(json_encode(['error' => 1, 'message' => 'Dangerous extension'])); } } ## MIME 真实类型检查 $finfo = new finfo(FILEINFO_MIME_TYPE); $mime = $finfo->file($_FILES['imgFile']['tmp_name']); $allowed_mimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (!in_array($mime, $allowed_mimes)) { die(json_encode(['error' => 1, 'message' => 'Invalid MIME type'])); } ## file_manager_json.php 加登录验证 file_manager_json.php 头部加: session_start(); if (empty($_SESSION['user_id']) || $_SESSION['user_role'] !== 'admin') { header('HTTP/1.1 403 Forbidden'); exit; } 这一项至关重要——之前发现过黑产专门扫描 KindEditor 的 file_manager_json.php,匿名拉走整个 attached/ 目录的图片清单做爬虫源。 ## 多语言、皮肤、自定义工具栏的扩展 ## 切换中文界面 KindEditor.create('#editor', { langType: 'zh-CN', themeType: 'default' }); 同时确保 lang/zh-CN.js 文件存在。如果想自定义某些菜单文案,直接编辑这个文件,热更新无需重启。 ## 定制工具栏按钮顺序 KindEditor.create('#editor', { items: [ 'source', '|', 'undo', 'redo', '|', 'cut', 'copy', 'paste', 'plainpaste', 'wordpaste', '|', 'justifyleft', 'justifycenter', 'justifyright', '|', 'insertorderedlist', 'insertunorderedlist', '|', 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold', 'italic', 'underline', '|', 'image', 'multiimage', 'flash', 'media', '|', 'table', 'hr', 'emoticons', '|', 'link', 'unlink' ] }); 移除掉“网络图片”“图片空间”“flash”等不常用功能,工具栏更整洁,同时降低被滥用的风险。 ## 与 WordPress / Discuz / DedeCMS 的具体集成 ## WordPress 后台用 KindEditor 替换 TinyMCE 不建议这么做。WordPress 5.0 之后默认编辑器是 Gutenberg block editor,KindEditor 是 2010 年代设计的传统富文本,强行替换会丢失所有 block 功能。如果非要用,参考 wp-kindeditor 这类老旧插件。 ## DedeCMS 把内置编辑器换成 KindEditor 编辑 include/inc/inc_fun_funAdmin.php 找到 ShowEditor 函数,替换 fck/ueditor 的调用为 KindEditor。具体步骤涉及 JS path 调整、上传后端对接 dedeAjax,非常繁琐,仅在二次开发项目里值得做。 ## Discuz 集成 Discuz 默认编辑器是自家的 ckeditor 改造版。强行替换会破坏帖子 BBCode 解析。建议只在自定义页面(比如插件后台)里用 KindEditor,原帖编辑器保留 Discuz 默认。 ## 实战故障排查 ## 故障 1:图片上传成功但前台显示裂图 查后端返回的 JSON: {"error":0,"url":"/kindeditor/attached/202405/test.jpg"} 看 url 字段——如果是相对路径但你的页面在子目录,会拼错。修复 upload_json.php 让 url 始终返回绝对路径: $file_url = ($_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $save_url . $new_file_name; echo json_encode(['error' => 0, 'url' => $file_url]); ## 故障 2:上传 50 张图后浏览器卡死 一次性把所有图加入 DOM 让浏览器渲染压力爆炸。改造 multiimage.js 让它分批 5 张一次插入: function batchInsert(files, idx) { if (idx >= files.length) return; var batch = files.slice(idx, idx + 5); insertImages(batch); setTimeout(function() { batchInsert(files, idx + 5); }, 200); } ## 故障 3:粘贴 Word 文档进编辑器后样式爆炸 Word 粘贴的 HTML 里夹杂了大量 mso-* 内联样式与命名空间。KindEditor 自带 wordpaste 按钮可以清理,但默认未启用。在 items 工具栏配置里把 wordpaste 加进去,并设置 pasteType 配置: KindEditor.create('#editor', { pasteType: 1, // 0=禁止粘贴, 1=纯文本粘贴, 2=允许 HTML 粘贴 afterCreate: function() { this.cmd.range.addRange(/* ... */); } }); ## 故障 4:移动端编辑器无法拖拽图片上传 KindEditor 4.1.x 没有针对移动端优化,多图上传依赖 input[type=file][multiple],移动浏览器对此支持差。建议移动端改用 wangEditor 或 Quill 这类现代编辑器。 ## 故障 5:上传后图片自动旋转 90° iPhone 拍的横屏照片 EXIF 里有旋转信息,浏览器渲染前会按 EXIF 旋转。后端用 GD 重新保存时丢失 EXIF,前端再加载就“转向不对”。修复:上传时同步处理 EXIF: $exif = exif_read_data($_FILES['imgFile']['tmp_name']); if (!empty($exif['Orientation'])) { $img = imagecreatefromjpeg($_FILES['imgFile']['tmp_name']); switch ($exif['Orientation']) { case 3: $img = imagerotate($img, 180, 0); break; case 6: $img = imagerotate($img, -90, 0); break; case 8: $img = imagerotate($img, 90, 0); break; } imagejpeg($img, $save_path . $new_file_name, 90); } ## 常见问题解答 ## KindEditor 还在维护吗? 原作者 Roddy 自 2014 年后已不再活跃维护。最后一个版本是 4.1.12(2017 年)。如果是新项目,建议选择 wangEditor、CKEditor 5、TinyMCE 6、Quill 这类有持续更新的现代编辑器。如果是老项目里已经用了 KindEditor,本文的改造方案足够覆盖大多数运营需求。 ## upload_json.php 改了之后多图上传仍然走老目录? 多图上传走的是同一个 upload_json.php,但路径计算可能因为 PHP_SELF 不同而走到不同的相对路径分支。建议改造时把所有 save_path/save_url 计算都改用绝对路径,避免分支不一致。 ## 批量上传 200 张图速度很慢怎么办? 瓶颈通常在后端 GD 库处理图片。三个加速:上传时不做缩略图(异步交给 cron);用 Imagick 替代 GD(处理速度快 30%);前端先压缩再上传(用 browser-image-compression 这类 JS 库)。 ## KindEditor 与 fileinput.js 哪个上传体验更好? fileinput.js 的 UI 更现代,支持拖拽、进度条、缩略图预览,移动端友好。但它只是单纯文件上传组件,不带富文本编辑功能。如果业务只是“上传图片”可以用 fileinput.js 替代 KindEditor 的 multiimage 模块。 ## 能否把 KindEditor 切换成 markdown 编辑器? 切换成本接近重写。建议直接用 SimpleMDE、EasyMDE、Editor.md 这种专业 markdown 编辑器,KindEditor 强行支持 markdown 体验差。 ## file_manager_json.php 不加权限校验有多严重? 非常严重。曾遇到过实战案例:黑产扫到未保护的 file_manager_json.php,遍历整个 attached/ 目录找用户上传的身份证、合同截图,扒走数据库。所有线上 KindEditor 部署都必须加管理员权限校验。 ## 升级 PHP 到 7.4+ 后上传报错? KindEditor 4.1.12 的 PHP 后端用了一些 PHP 7+ 移除的写法(each() 函数、__autoload)。需要手动改成 foreach、spl_autoload_register 替代。或者干脆把 upload_json.php 重写成现代 PHP 版本(200 行内)。 ## 多个站点共用一个 KindEditor 部署可行吗? 可行,但 upload_json.php 必须按 Host 区分上传目录,否则站点 A 上传的图能被站点 B 访问。在 upload_json.php 顶部加:$host_dir = preg_replace('/[^a-z0-9_.-]/i', '_', $_SERVER['HTTP_HOST']); $save_path .= $host_dir . '/'; ## KindEditor 的 SVG 上传支持吗? 默认扩展名白名单不含 svg。手动加上后要注意:SVG 文件可以包含