news 2026/10/16 2:56:23

CKEditor 4 imagebase 插件上传功能手动测试指南:从文件拖放到占位 Widget 创建

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CKEditor 4 imagebase 插件上传功能手动测试指南:从文件拖放到占位 Widget 创建
  • 前端
  • UI组件

【免费下载链接】ckeditor4

The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.

项目地址:https://gitcode.com/gh_mirrors/ck/ckeditor4
点击查看免费下载

导读

本文以仓库中 imagebase 插件 Upload 功能的手动测试文档(tests/plugins/imagebase/features/manual/upload.md)为主线,系统讲解 CKEditor 4 中"将本地文件直接拖入编辑区、自动创建占位 Widget 并显示文件名"这一上传链路的测试场景与底层实现。读完本文,你将掌握该手动测试的步骤、预期结果、bender 测试元数据的含义,并能结合 imagebase 插件源码 理解文件类型匹配、FileLoader 上传、进度条渲染与 Cloud Services 鉴权等核心机制,从而具备自行复现、验证与扩展该上传功能的能力。

测试文档定位:一段面向人工验证的 Upload 功能用例

upload.md是 CKEditor 4 基于 Bender 测试框架的手动测试(manual test)用例,归属于 imagebase 插件的 features 目录。其完整内容围绕一个核心交互场景展开:

测试步骤:从文件系统拖拽任意文件到编辑区(editable)中。预期结果:创建一个占位 Widget(placeholder widget),其中包含文件名。注意事项:截至文档编写时,Cloud Services 只接受图片资源,对其他资源类型会返回400状态码;Cloud Services 实际返回的 URL 可以在浏览器控制台日志中查看。

这段描述虽然简短,但它浓缩了三条关键技术事实:

  1. 触发方式是"拖放文件"(drag and drop),对应 imagebase 的uploadfeature 在编辑器paste事件上的监听逻辑;
  2. 产物形态是"包含文件名的 placeholder widget",即上传开始阶段先用本地 blob URL 渲染一个占位 Widget,而非直接插入最终图片;
  3. 后端约束是 Cloud Services 网关仅接受图片 MIME 类型,其余类型返回400,这决定了该功能在默认配置下对非图片文件的失败路径。

文档头部还携带了 Bender 测试框架所需的元数据,是运行与筛选该测试的关键:

@bender-tags: 4.9.0, feature, 932 @bender-ui: collapsed @bender-ckeditor-plugins: wysiwygarea, toolbar, imagebase, elementspath, placeholder, cloudservices, easyimage @bender-include: %BASE_PATH%/plugins/easyimage/_helpers/tools.js

其中@bender-tags声明了该测试自 4.9.0 版本引入、属于 feature 类测试,关联内部 issue 932;@bender-ckeditor-plugins列出测试编辑器需要加载的插件集合;@bender-include引入测试辅助脚本(实际文件位于 tests/plugins/easyimage/_helpers/tools.js,其中定义了CLOUD_SERVICES_UPLOAD_GATEWAY与CLOUD_SERVICES_TOKEN_URL两个测试用的云端地址常量);@bender-ui: collapsed则控制测试页面在 Bender 结果面板中的展示方式。

手动测试页面的完整实现

与upload.md配套的页面源码是 tests/plugins/imagebase/features/manual/upload.html,它给出了该测试的可运行环境。核心代码可以拆成三层:

第一层:编辑器初始化与云端配置。页面创建一个经典编辑器,通过cloudServices_uploadUrl与cloudServices_tokenUrl接入 Cloud Services 网关,并加载 easyimage 的内容样式:

var editor = CKEDITOR.replace( 'classic', { cloudServices_uploadUrl: easyImageTools.CLOUD_SERVICES_UPLOAD_GATEWAY, cloudServices_tokenUrl: easyImageTools.CLOUD_SERVICES_TOKEN_URL, height: 500, contentsCss: '/apps/ckeditor/plugins/easyimage/styles/easyimage.css' } );

第二层:为 placeholder Widget 挂载 upload feature。关键代码监听widgetDefinition事件,在 placeholder 的定义上调用CKEDITOR.plugins.imagebase.addFeature( this, 'upload', def ),并进一步定制三个上传相关的属性:

if ( def.name === 'placeholder' ) { var ret = CKEDITOR.plugins.imagebase.addFeature( this, 'upload', def ), i; for ( i in ret ) { def[ i ] = ret[ i ]; } // 使用 Cloud Services 专用的 Loader 类型。 def.loaderType = CKEDITOR.plugins.cloudservices.cloudServicesLoader; // 匹配任意文件类型(正则 /^.*$/)。 def.supportedTypes = /^.*$/; def.progressReporterType = CKEDITOR.plugins.imagebase.progressBar; }

注意这里supportedTypes被设置为/^.*$/,这正是"拖入任意文件都会创建占位 Widget"的根源——与 easyimage 插件 默认只接受image/(jpeg|png|gif|bmp)的正则形成鲜明对比。

第三层:用上传事件驱动 Widget 内容。页面在instanceReady后监听instanceCreated,为每个 placeholder 实例绑定uploadStarted/uploadDone/uploadFailed三个一次性事件,实现"上传中显示 Uploading...、成功后显示文件名、失败后显示错误码"的完整状态机:

widget.once( 'uploadStarted', function() { widget.setData( 'name', 'Uploading...' ); } ); widget.once( 'uploadDone', function( uploadEvent ) { var loader = uploadEvent.data.loader; widget.setData( 'name', 'File "' + loader.file.name + '" uploaded!' ); console.log( 'File ' + loader.file.name + ' can be found at ' + loader.responseData.response[ 'default' ] ); } );

console.log中打印的response[ 'default' ]就是upload.md末尾提示"可以在控制台日志中找到 Cloud Services 实际返回 URL"的来源——easyimage 的服务端响应以default字段给出默认尺寸图片地址(参见 plugins/easyimage/plugin.js 中的_parseSrcSet与uploadDone处理逻辑)。

页面开头还有两行环境守卫:移动端因缺少文件拖放支持直接bender.ignore()跳过,bender.tools.ignoreUnsupportedEnvironment( 'easyimage' )则在 unsupported 环境(如 IE10 及以下,见 easyimage 插件的isSupportedEnvironment)中忽略该测试。

底层原理:imagebase 的 upload feature 是如何工作的

占位 Widget 能"凭空"出现,靠的是 imagebase 插件内置的uploadfeature(定义见 plugins/imagebase/plugin.js 的getUploadFeature)。它是一个以 mixin 方式提供给 Widget 定义的私有 API,通过addFeature注入到目标 Widget 上。其核心链路如下。

1. setUp:拦截 paste 事件并过滤文件

feature 的setUp在编辑器上注册paste监听。当dataTransfer.isFileTransfer()为真且编辑器非只读时,遍历拖入的文件,用definition.supportedTypes正则逐一匹配 MIME 类型,收集所有命中文件后取消默认粘贴行为:

if ( editor.readOnly || !isFileTransfer ) { return; } for ( var i = 0; i < filesCount; i++ ) { curFile = dataTransfer.getFile( i ); if ( CKEDITOR.fileTools.isTypeSupported( curFile, definition.supportedTypes ) ) { matchedFiles.push( curFile ); } } if ( matchedFiles.length === 0 ) { return; } evt.cancel(); evt.stop();

从 tests/plugins/imagebase/features/upload.js 的自动化用例可以验证该过滤行为:test dropping supported file type creates a widget断言命中 1 个 Widget,而test dropping unsupported file does not create a widget断言 0 个;test dropping file into a readonly does not create a widget则验证只读状态下不会创建 Widget。

2. 创建 FileLoader 并插入占位 Widget

对每个匹配文件,feature 依次调用两个私有方法:

  • _spawnLoader( editor, curFile, definition, curFile.name ):通过editor.uploadRepository.create()创建CKEDITOR.fileTools.fileLoader实例(loaderType 可定制,默认CKEDITOR.fileTools.fileLoader),并调用loader loadMethod发起加载与上传。loadMethod默认为loadAndUpload,其行为定义在 plugins/filetools/plugin.js 中:先load()用 FileReader 把文件读入内存,再链式触发upload()发送 XHR。
  • _insertWidget( editor, definition, URL.createObjectURL( curFile ), true, { uploadId: loader.id } ):用URL.createObjectURL()生成的本地 blob URL 作为临时src,按 Widget 定义中的模板输出 HTML,包装为 Widget 并初始化,同时把uploadId写入 Widget 数据,建立 Widget 与 Loader 的关联。

多文件拖入时,代码还通过放大并折叠选区的方式调整光标位置,确保每个文件生成独立的 Widget 且最后一个保持选中状态。

3. init 与 _beginUpload:绑定事件与进度条

Widget 实例在ready后读取data.uploadId,在editor.uploadRepository.loaders中找回对应的 Loader,随即调用_beginUpload。该方法是整个上传状态机的枢纽:

widget.setData( 'uploadId', loader.id ); if ( widget.fire( 'uploadStarted', loader ) !== false && widget.progressReporterType ) { if ( !widget._isLoaderDone( loader ) ) { widget.wrapper.addClass( 'cke_widget_wrapper_uploading' ); var progress = new widget.progressReporterType(); widget.wrapper.append( progress.wrapper ); progress.bindLoader( loader ); } else { // ... } }

它同时注册了loader.on( 'abort' | 'error' | 'uploaded' )三个监听,分别映射到failHandling与uploadComplete:

  • 上传失败或中止时,清理cke_widget_wrapper_uploading类,触发uploadFailed事件;若该事件未被cancel,默认行为是调用editor.widgets.del( widget )直接删除 Widget;
  • 上传成功时触发uploadDone事件,交由业务代码(如 easyimage 或手动测试页面)把 blob URL 替换为服务端返回的最终地址。

这些事件在 upload.js 中有逐项断言:test events验证成功路径只触发uploadStarted与uploadDone;test upload error验证失败路径触发uploadFailed且 Widget 被移除;test upload error event is cancelable验证cancel后可保留 Widget(手动测试页面正是用这一机制把失败信息写进占位符文本)。

4. 进度条:progressReporterType 与 progressBar

_beginUpload中widget.progressReporterType默认指向CKEDITOR.plugins.imagebase.progressBar(见 plugins/imagebase/plugin.js 的ProgressBar)。它渲染一个.cke_loader > .cke_bar结构,updated( progress )按 0~1 的进度值换算百分比并写入width样式;bindLoader则把进度条与 Loader 的update事件挂钩,并自动在abort/error/uploaded时收尾。进度刷新频率受UPLOAD_PROGRESS_THROTTLING = 100(毫秒)节流控制。

值得注意,progressReporterType可以被设为false或null以完全禁用进度条,也可以在uploadStarted事件中evt.cancel()阻止默认进度条创建——自动化测试test preventing default progress reporter with event与test preventing default progress reporter with widgetDefinition.progressReporterType分别覆盖了这两种方式。若想自定义指示器形态,可以参考 tests/plugins/imagebase/features/_helpers/tools.js 中基于progressReporter扩展出的遮罩进度(getProgressOverlapType)与圆形进度(getProgressCircleType),其交互演示见 tests/plugins/imagebase/manual/progressbar.html。

Cloud Services 集成与 400 响应

upload.md特别强调"Cloud Services 目前只接受图片,其他资源类型返回 400",这与 cloudservices 插件的实现紧密相关。在 plugins/cloudservices/plugin.js 中:

  • cloudServicesLoader是fileLoader的子类,upload()未传 URL 时回退到editor.config.cloudServices_uploadUrl,缺失则抛出cloudservices-no-upload-url错误;
  • 插件在beforeInit阶段通过cloudServices_tokenUrl拉取安全令牌,并每隔REFRESH_INTERVAL(默认 3600000ms,即 1 小时)自动刷新;未配置 token URL 会抛出cloudservices-no-token-url错误;
  • 发送请求时,插件把上传字段从upload重命名为file,并以Authorization请求头携带令牌;响应则拦截fileUploadResponse事件,将 JSON 响应体原样解析为loader.responseData.response。

因此,当向该网关拖入非图片文件时,服务端返回400,Loader 触发error状态,Widget 走uploadFailed分支——手动测试页面会把它渲染为Uploading "xxx" failed (code 400) 😢,这正是upload.md中"返回 400 代码"在 UI 上的可见表现。

顺带一提,uploadsimple.html 是同一 feature 的精简演示:它只接受image/(jpeg|png),用div而非 placeholder 作为 Widget 模板,通过uploadDone/uploadFailed直接把文件名写入元素文本,可作为理解"自定义 Widget + upload feature"的最小示例。

自动化测试佐证:upload feature 的边界行为

除了手动测试,imagebase 还提供了覆盖度很高的自动化测试 tests/plugins/imagebase/features/upload.js,可用来交叉验证本文所述行为:

测试用例验证点
test dropping supported file type creates a widget支持的 MIME 类型生成对应 Widget
test dropping unsupported file does not create a widget不支持的 MIME 类型不生成 Widget
test pasting supported file alongside HTML content does not create a widget文件与 HTML 混贴时不触发上传
test multiple files create multiple widgets多文件一次拖入生成多个 Widget
test loader can be customizedloaderType可替换为自定义 Loader
test events/test upload erroruploadStarted、uploadDone、uploadFailed触发路径
test default progress reporter/test progress reporter customization默认与自定义进度条
test widget gets class during the upload上传中cke_widget_wrapper_uploading类存在,完成后移除
test copy and paste file during upload上传中复制粘贴 Widget 会共享 Loader 并继续完成上传

这些用例同时印证了upload.md未直接写出但真实存在的边界:上传中的 Widget 被复制粘贴到同一编辑器甚至另一个编辑器时,新 Widget 会依据uploadId找到对应 Loader 并继续上传(参见test copy in-progress widget into a different editor)。

如何运行与验证

  1. 准备 Bender 环境:仓库根目录的 bender.js、bender.ci.js 与 bender-runner.config.json 提供了测试运行配置;手动测试通常在bender server启动后于浏览器中访问对应页面运行。
  2. 加载目标用例:打开 upload.html,页面会自动创建带 imagebase、placeholder、cloudservices、easyimage 等插件的编辑器。
  3. 执行测试:从本地文件系统拖拽任意文件到编辑区;观察是否生成黄色背景的占位 Widget、其文本是否变为文件名;打开浏览器控制台查看 Cloud Services 返回的真实 URL。
  4. 验证失败路径:拖入非图片文件,预期 Widget 显示400错误信息;拖入图片则上传成功后占位内容被替换为File "xxx" uploaded!。
  5. 对照自动化结果:运行 features/upload.js 可一键回归上述所有边界行为(成功、失败、多文件、只读、复制粘贴、进度条等),与手动测试互为补充。

需要注意,该手动测试依赖 Cloud Services 测试网关(URL 常量定义在 tests/plugins/easyimage/_helpers/tools.js),属于在线服务;若离线环境,可参考uploadsimple页面将loaderType替换为自定义 Loader 并指向本地上传接口,或参考 plugins/filetools/plugin.js 中fileUploadRequest/fileUploadResponse事件自行实现上传响应解析。

  • 前端
  • UI组件

【免费下载链接】ckeditor4

The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.

项目地址:https://gitcode.com/gh_mirrors/ck/ckeditor4
点击查看免费下载

相关推荐

上一篇:Wasmtime 测试用例缩减实战:从 wasm-tools shrink 到 bugpoint 的完整工具链与谓词编写指南
下一篇:react-admin `<Breadcrumb>` 组件完全指南:基于 App Location 的自适应导航面包屑

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 2:56:00

靠谱智媒营销平台怎么挑?朝闻通金理奖资质优势体现在哪里?

在存量营销时代&#xff0c;粗放式硬广投放的边际效益持续递减&#xff0c;以权威背书、内容种草、长效声量沉淀为核心的软文推广&#xff0c;已然成为企业品牌建设、市场拓客、口碑维稳、资本市场形象塑造的核心刚需。相较于传统营销模式&#xff0c;软文推广凭借高可信度、长…

作者头像 李华
网站建设 2026/10/16 2:55:17

你的AI编程历史其实很值钱:开源项目sell-sessions-skill完全指南

【免费下载链接】sell-sessions-skill 项目地址&#xff1a; https://gitcode.com/gh_mirrors/se/sell-sessions-skill 点击查看 免费下载 每天用 Claude Code、Codex 写代码&#xff0c;这些 AI 编程会话历史其实很值钱。sell-sessions-skill 是一个开源的 AI 会话授权出售工…

作者头像 李华
网站建设 2026/10/16 2:54:49

Windows 11 激活完整指南:MAS 免费激活器 3 分钟搞定免密钥

Windows 11 激活完整指南&#xff1a;MAS 免费激活器 3 分钟搞定免密钥 【免费下载链接】Microsoft-Activation-Scripts Open-source Windows and Office activator featuring HWID, Ohook, TSforge, and Online KMS activation methods, along with advanced troubleshooting.…

作者头像 李华
网站建设 2026/10/16 2:53:48

医疗器械元器件采购:可靠性验证的5个硬门槛

医疗器械上的一颗元器件失效&#xff0c;后果不是返修&#xff0c;可能是患者安全事件和产品召回。医疗器械元器件采购的严格程度仅次于军品和车规&#xff0c;但比车规多了一层"生物相容性法规合规"。想进入医疗供应链&#xff0c;供应商要先过5个硬门槛。一、5 个硬…

作者头像 李华