• 前端
  • UI组件

【免费下载链接】ckeditor4

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

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

isFileTransfer() 是 CKEditor 4.17.0 在 clipboard 插件中新增的 DataTransfer 判定 API,用于判断一次粘贴或拖放操作携带的剪贴板数据是否为"纯文件"传输。本文以 isfiletransfer 手动测试文档 为骨架,结合 clipboard 插件源码 与 datatransfer 单元测试 的底层实现,讲解该 API 的判定逻辑、在 paste 事件中的接入方式、四类典型场景的手动验证方法,以及它如何支撑图片自动转 base64 插入等功能。读完本文,你将能在自己的插件或页面脚本中可靠地区分"文件型"与"非文件型"剪贴板操作。

isFileTransfer() 是什么

isFileTransfer() 是 CKEDITOR.plugins.clipboard.dataTransfer 对象上的一个实例方法,自 CKEditor 4.17.0 起提供(对应 Issue #4604)。它的语义非常明确:判断当前 DataTransfer 对象中是否只包含文件(files)这一种数据,而不包含文本、HTML、URL 等其他 MIME 类型。

dataTransfer.isFileTransfer(); // true 表示纯文件传输,false 表示含非文件数据

该方法出现在手动测试文档的 bender 元数据中:

@bender-tags: 4.17.0, 4604, feature, clipboard

即该特性于 4.17.0 引入,测试标签为 feature 类型,归属于 clipboard 模块。

为什么需要这个 API

在编辑器实践中,"粘贴/拖入一张图片"与"粘贴/拖入一段文本"走的是完全不同的数据处理路径:文件需要被读取为 Blob/DataURL 再转成 img 标签,而文本需要经过 HTMlDataProcessor 清洗后插入。若无法区分二者,编辑器要么会把文件路径当作文本塞进内容,要么会把文本误当成文件处理。isFileTransfer() 正是为了给这类分支逻辑提供一个可靠、跨浏览器的判定入口。

源码级解析:判定逻辑如何实现

isFileTransfer() 的实现位于 plugins/clipboard/plugin.js#L2818-L2832:

/**
 * Checks if the data transfer contains only files.
 *
 * @since 4.17.0
 * @returns {Boolean} `true` if the object contains only files.
 */
isFileTransfer: function() {
	var types = this.getTypes(),
		// Firefox uses application/x-moz-file type for dropped local files.
		filteredTypes = CKEDITOR.tools.array.filter( types, function( type ) {
			return type !== 'application/x-moz-file';
		} );

	return filteredTypes.length === 1 && filteredTypes[ 0 ].toLowerCase() === 'files';
},

整个判定分三步:

  1. 获取 MIME 类型列表:调用 getTypes() 返回剪贴板中所有数据类型。
  2. 剔除 Firefox 特有类型:Firefox 在拖放本地文件时会额外写入 application/x-moz-file 类型,若不剔除,纯文件拖放会被误判为"多类型混合"。
  3. 归一化校验:过滤后必须恰好只剩一个类型,且该类型的小写形式等于 files,才返回 true。

这一设计意味着:只要剪贴板里同时存在 text/plain、text/html、URL、text/uri-list 等任一非文件类型,判定结果就是 false——即使其中确实包含文件。这正是"文件 + 文本混合"场景下编辑器应优先按文本处理的设计意图。

getTypes():类型列表从哪来

getTypes() 定义于 plugins/clipboard/plugin.js#L2880-L2896,自 4.13.1 起可用:

getTypes: function() {
	if ( this._.types.length > 0 ) {
		return this._.types;
	}

	if ( !this.$ || !this.$.types ) {
		return [];
	}

	return [].slice.call( this.$.types );
},

它优先返回 CKEditor 内部缓存(this._.types,由 cacheData() 填充),缓存为空时才去读取原生 dataTransfer.types。这样在异步访问原生 DataTransfer 已失效的场景下仍能拿到类型信息。

getFilesCount():与文件数配合使用

isFileTransfer() 只回答"是否纯文件",不回答"有几个文件"。文件数量由 getFilesCount() 提供(plugins/clipboard/plugin.js#L2778-L2795):

getFilesCount: function() {
	if ( this._.files.length ) {
		return this._.files.length;
	}

	// Only access .files once - it's expensive in Chrome (#4807).
	var files = this.$ && this.$.files || null;
	if ( files && files.length ) {
		return files.length;
	}

	return this._getImageFromClipboard() ? 1 : 0;
},

实现上有两个值得注意的细节:

  • Chrome 性能优化(#4807):原生 dataTransfer.files 访问开销高,代码刻意只读取一次并优先使用内部缓存。
  • Edge 兼容处理(#13755):Edge 存在 files 属性为空但无 length 的情况,因此需要回退到 items[0].getAsFile()(_getImageFromClipboard(),见 plugin.js#L2898-L2909)来判断是否存在图片文件。

在真实业务中,二者常组合使用,例如判断"恰好一张图片文件":

dataTransfer.isFileTransfer() && dataTransfer.getFilesCount() === 1

这正是 clipboard 插件内部 isFileData() 辅助函数所用的判断(见下文)。

在 paste 事件中接入:完整可运行示例

手动测试页 tests/plugins/clipboard/manual/isfiletransfer.html 给出了最小可复现的接入代码:监听编辑器的 paste 事件,读取 evt.data.dataTransfer,调用 isFileTransfer() 后把结果以"绿底/红底 + 文案"的形式展示在编辑器上方的消息区。

页面样式与结构:

<style>
	.ok {
		background: green;
	}
	.not-ok {
		background: red;
	}
</style>
<p id="message"></p>
<div id="editor">
	<p>Paste from clipboard here:</p>
</div>

核心脚本:

if ( CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) {
	bender.ignore();
}

CKEDITOR.replace( 'editor', {
	on: {
		paste: function( evt ) {
			var message = CKEDITOR.document.getById( 'message' ),
				isFileTransfer = evt.data.dataTransfer.isFileTransfer();

			message.removeAttribute( 'class' );
			message.addClass( isFileTransfer ? 'ok' : 'not-ok' );
			message.setText( isFileTransfer ? 'File pasted' : 'Non-file pasted' );
		}
	}
} );

代码要点:

  • evt.data.dataTransfer 就是本次粘贴/拖放操作的 CKEDITOR.plugins.clipboard.dataTransfer 封装对象,可直接调用 isFileTransfer()。
  • 通过切换 ok(绿)与 not-ok(红)两个 class 直观反馈判定结果。
  • 开头的 bender.ignore() 说明该测试在 IE 10 以下版本会被直接跳过,因为旧版 IE 不支持自定义 MIME 类型,文件粘贴判定不可靠(详见源码注释 #4612)。

在你的真实页面里,不需要 bender 相关代码,直接这样写即可:

CKEDITOR.replace( 'editor', {
	on: {
		paste: function( evt ) {
			var dataTransfer = evt.data.dataTransfer;
			if ( dataTransfer && dataTransfer.isFileTransfer() ) {
				// 纯文件粘贴:走文件上传/图片插入逻辑
			} else {
				// 文本或混合粘贴:走正常 HTML/文本处理逻辑
			}
		}
	}
} );

注意:paste 事件可能在没有原生 DataTransfer 的情况下触发,此时 clipboard 插件会自动构造一个空的 dataTransfer 对象(见 plugin.js#L304-L308),所以调用前通常无需判空。

四类验证场景(手动测试文档完整继承)

手动测试文档 isfiletransfer.md 设计了 4 个场景,覆盖"粘贴 vs 拖放"与"文件 vs 非文件"的笛卡尔组合。运行该测试页面(bender 手动测试模式)后,按下列步骤操作即可逐项验证:

Scenario 1:从剪贴板粘贴图片

  1. 复制一张图片(如在浏览器中复制图片,或使用系统截图后复制)。
  2. 将其粘贴(Ctrl/Cmd + V)到编辑器内。

预期结果:编辑器上方消息区显示 "File pasted",且背景为绿色。

注意事项:

  • 本测试不要求图片真的被插入编辑器内容区——只验证 isFileTransfer() 的判定结果。
  • IE 浏览器可能直接忽略这类粘贴操作(旧版 IE 对剪贴板自定义类型的支持有限),属于已知行为。

Scenario 2:粘贴非文件内容(如文本)

  1. 复制一段纯文本。
  2. 粘贴到编辑器内。

预期结果:消息区显示 "Non-file pasted",背景为红色。

文本粘贴时,DataTransfer 中只存在 text/plain(或 text/html)类型,过滤后类型数不为 1 或类型名不是 files,因此判定为 false。

Scenario 3:拖放图片或其他文件

  1. 从文件管理器/桌面拖拽一张图片或任意文件。
  2. 将其拖放到编辑器内。

预期结果:消息区显示 "File pasted",背景为绿色。

拖放文件时,原生 DataTransfer 只包含 files 类型(Firefox 会附加 application/x-moz-file,但已被实现中的过滤器剔除),因此判定为 true。

注意事项:

  • 同样不要求文件真的被插入编辑器,只验证判定逻辑。
  • 若测试页面中同时启用了其他处理文件拖放的逻辑,可能出现消息与插入行为并行的现象,属正常。

Scenario 4:拖放非文件内容(如文本)

  1. 用鼠标选中一段文本并拖拽。
  2. 将其拖放到编辑器内。

预期结果:消息区显示 "Non-file pasted",背景为红色。

文本拖放产生的 DataTransfer 包含文本类 MIME 类型,isFileTransfer() 返回 false。

场景对照表

场景操作判定结果预期提示
1粘贴图片trueFile pasted(绿)
2粘贴文本falseNon-file pasted(红)
3拖放文件trueFile pasted(绿)
4拖放文本falseNon-file pasted(红)

单元测试:四条断言覆盖关键边界

除手动测试外,仓库还提供了一组针对 isFileTransfer() 的自动化单元测试,位于 tests/plugins/clipboard/datatransfer.js#L1314-L1375,通过 bender.tools.mockNativeDataTransfer() 模拟原生 DataTransfer 来验证边界行为:

  1. 仅含文件 → true:types 中只有 Files,files 中有多个文件,断言 isFileTransfer() 为 true。注意该用例在浏览器不支持自定义数据类型时会被忽略(assert.ignore())。
  2. 仅含文本 → false:只设置 text/html 数据,断言为 false。
  3. 文件 + 文本混合 → false:同时存在 Files 与 text/html,断言为 false——印证"必须恰好只有 files 一种类型"的核心规则。
  4. Firefox 文件类型 → true:types 同时包含 application/x-moz-file 与 Files,在 gecko 内核浏览器中断言为 true——验证对 Firefox 特有类型的过滤逻辑。

这些用例是理解 isFileTransfer() 行为契约的最佳参考:只要剪贴板中存在任何非文件类型,结果即为 false;Firefox 的 application/x-moz-file 是唯一被显式豁免的类型。

内部应用:isFileData() 与图片转 base64 流程

isFileTransfer() 不只是给开发者用的公共 API,它本身就是 clipboard 插件"粘贴/拖放图片自动转为 base64"功能的核心判定依据。相关逻辑位于 plugins/clipboard/plugin.js#L186-L236 与辅助函数 isFileData()(plugin.js#L280-L294)。

function isFileData( evt, dataTransfer ) {
	// Checking for fileTransferCancel on IE to prevent comparing empty string
	// from dataTransfer.id and falling into infinite loop (#4681).
	if ( CKEDITOR.env.ie && evt.data.fileTransferCancel ) {
		return false;
	}

	if ( !CKEDITOR.env.ie && ( !dataTransfer || latestId === dataTransfer.id ) ) {
		return false;
	}

	return dataTransfer.isFileTransfer() && dataTransfer.getFilesCount() === 1;
}

图片处理流程(优先级为 1 的 paste 监听器):

  1. 若当前环境支持文件粘贴(isCustomDataTypesSupported || isFileApiSupported)且配置 clipboard_handleImages 为 true,进入处理;
  2. 若 dataValue 已有内容则直接跳过——已有数据优先于文件;
  3. 调用 isFileData() 确认这是"纯文件且恰好一个文件"的传输;
  4. 用 testImageBase64Support() 检查文件类型是否属于 image/png、image/jpeg、image/gif(见 plugin.js#L238-L246);
  5. 通过 FileReader.readAsDataURL() 异步读取文件,成功后以 <img src="base64"> 作为 dataValue 重新触发 paste 事件;
  6. 读取失败或被中止时,调用 setCustomIEEventAttribute() 设置 fileTransferCancel 标记后继续正常流程,避免 IE 上因 dataTransfer.id 与空字符串比较而陷入无限循环(#4681)。

可以看到,isFileTransfer() 在此处的角色是文件粘贴流程的第一道闸门:只有它返回 true 且文件数为 1,编辑器才会尝试把剪贴板里的图片文件转换为内嵌 base64 图片。

相关配置项

与文件粘贴/拖放处理密切相关的两个配置均在 plugins/clipboard/plugin.js#L3553-L3570 定义:

配置项默认值引入版本作用
clipboard_handleImagestrue4.17.0是否由 clipboard 插件接管图片的粘贴与拖放,在支持 File API 的浏览器上将图片转为 base64 字符串
clipboard_notificationDuration10000(毫秒)4.7.0粘贴被浏览器拦截或存在不支持文件类型时,通知的展示时长

若你的编辑器不需要自动处理图片文件(例如已有独立的上传组件),可以关闭图片接管:

CKEDITOR.replace( 'editor', {
	clipboard_handleImages: false
} );

关闭后,图片文件的粘贴/拖放将不再被插件自动转成 base64 插入,paste 事件中 dataValue 保持为空,此时 isFileTransfer() 依然可以正常工作,供你自行决定如何处理文件。

浏览器兼容性与注意事项

综合源码注释与测试元数据,使用 isFileTransfer() 时需要注意:

  • IE < 10:不支持自定义 MIME 类型(见 plugin.js#L153-L156 的 #4612 说明),文件粘贴判定不可靠,手动测试在 IE < 10 下直接跳过;IE 还可能忽略"粘贴图片"这类操作。
  • IE 的防循环处理:IE 上文件读取失败/中止后会设置 fileTransferCancel 标记(#4681),涉及文件处理的监听器应关注该标记。
  • Firefox:拖放本地文件时类型列表中包含 application/x-moz-file,isFileTransfer() 已内部过滤,无需外部处理;另外 Firefox on Linux 会把文件路径以 file:// 形式写入 text/plain,clipboard 插件在文件存在时会隐藏该文本数据(见 plugin.js#L2632-L2638)。
  • Chrome:原生 dataTransfer.files 访问开销大(#4807),高频调用时优先使用缓存后的 getFile()/getFilesCount()。
  • Edge:存在 files 属性为空数组的情况(#13755),判定文件数时需要回退到 items[0].getAsFile()。
  • 判定严格性:isFileTransfer() 要求剪贴板中只有 files 一种类型。部分来源(如某些网页复制)可能同时携带文件与文本,此时应视为非文件传输处理。

总结

isFileTransfer() 是 CKEditor 4.17.0 提供的剪贴板数据判定 API,通过"过滤 Firefox 的 application/x-moz-file 后仅剩 files 单一类型"这一规则,可靠区分文件型与非文件型的粘贴/拖放操作。从 手动测试文档 的四个场景、HTML 测试页 的最小示例,到 datatransfer.js 的四条单元断言,再到 clipboard 插件 内部 isFileData() 对图片转 base64 流程的支撑,该 API 构成了 CKEditor 4 文件粘贴处理链路中承上启下的关键一环。开发者在实现"粘贴图片直接插入""拖放文件上传"等能力时,可以放心以 dataTransfer.isFileTransfer() 作为入口判定,并配合 getFilesCount()、clipboard_handleImages 等 API 与配置,构建完整的文件处理管线。

  • 前端
  • UI组件

【免费下载链接】ckeditor4

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

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

更多推荐