Electron clipboard 模块实战指南:基于 W3C Clipboard API 的系统剪贴板操作与自定义 MIME 格式
【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron
Electron 的clipboard模块运行在主进程,是当前版本中面向系统剪贴板进行复制/粘贴的唯一官方入口。本篇以 clipboard API 文档 为核心,完整覆盖其 W3C 风格方法(read/write/readText/writeText/has/clear)、Electron 自定义 MIME 格式(bookmark、findtext、osclipboard、web前缀)以及 Linux 特有的selection剪贴板,并结合仓库中的 JavaScript 封装层(lib/browser/api/clipboard.ts)与 C++ 原生实现(shell/browser/api/electron_api_clipboard.cc)说明底层调用链,读完后你可以掌握跨平台多格式剪贴板读写、原子写入与原生格式穿透(raw format round-trip)的完整实现方案。
模块定位与设计模型
clipboard模块的设计目标是复刻 W3C Clipboard API(navigator.clipboard)的接口形态,但提供的是不受隐私沙箱限制的原生剪贴板访问能力:
clipboard.read()返回Promise<ClipboardItem[]>,其中ClipboardItem携带一个或多个 MIME 类型到Blob负载的映射;clipboard.write()接受ClipboardItem[]数组,所有条目在一次调用内原子性地提交到系统剪贴板;- 与渲染进程的
navigator.clipboard不同,主进程的clipboard能读到文件的真实绝对路径(例如text/uri-list不做隐私脱敏),并且能访问平台级原始剪贴板格式。
需要注意一条已废弃行为(见 docs/api/clipboard.md 中的 history 注释):在渲染进程中直接使用clipboardAPI 已被废弃,该模块只在主进程中可用,进程模型定义参见 glossary。
[!NOTE]
ClipboardItem不能被用户代码继承(Electron 内置类的通用限制),详见 FAQ。
Electron 自定义 MIME 格式
除了标准 MIME 类型(text/plain、text/html、text/rtf、image/png、image/jpeg等),Electron 暴露了一小组自定义格式,遵循 W3C 自定义格式提案,但使用electron前缀而非web以避免命名冲突:
| 自定义格式 | 平台 | 说明 |
|---|---|---|
electron application/bookmark | 全平台(读取侧 Linux 不支持) | URL 书签。唯一例外:读写两侧都是ClipboardBookmark对象({ title, url })而非Blob,即getType('electron application/bookmark')解析为对象 |
electron application/findtext | 仅 macOS | 活跃应用"查找"粘贴板(find pasteboard)的内容 |
electron application/osclipboard;format="<name>" | 全平台 | 平台特定剪贴板格式的原始负载。<name>是平台格式名(Windows 如HTML Format,macOS 如public.utf8-plain-text)。clipboard.read()还会把没有标准 MIME 映射的任意平台格式归入该自定义格式暴露,因此原始 OS 格式可以"原样往返"(写进去和读出来是同一个 MIME 字符串) |
此外,read()和write()都接受任意 MIME 类型,包括以web前缀(后跟空格,如web application/x.my-format)开头的 W3C web 自定义格式:
const { clipboard, ClipboardItem } = require('electron') async function writeClipboard () { await clipboard.write([ new ClipboardItem({ 'web application/x.my-app-clip': new Blob(['arbitrary payload']) }) ]) } writeClipboard()核心方法详解
clipboard.readText()
返回Promise<string>,以纯文本形式读取剪贴板内容,对标 W3Cnavigator.clipboard.readText:
const { clipboard } = require('electron') async function readText () { await clipboard.writeText('hello i am a bit of text!') const text = await clipboard.readText() console.log(text) // 'hello i am a bit of text!' } readText()从源码结构看,electron_api_clipboard.cc 中Clipboard::ReadText是常见调用的"快速路径",直接走ui::Clipboard::ReadText,绕过了getType()的逐 MIME 分派开销;在 Windows 上若 Unicode 读取为空,还会自动回退到ReadAsciiText(ReadText中的IS_WIN分支)。
clipboard.writeText(text)
textstring
返回Promise<void>,文本写入完成时 resolve,对标 W3Cnavigator.clipboard.writeText:
const { clipboard } = require('electron') async function writeClipboardText () { await clipboard.writeText('hello i am a bit of text!') } writeClipboardText()clipboard.read()
返回Promise<ClipboardItem[]>,resolve 为携带剪贴板全部内容的ClipboardItem数组:
const { clipboard } = require('electron') async function dumpClipboard () { const items = await clipboard.read() for (const item of items) { for (const type of item.types) { const blob = await item.getType(type) console.log(type, blob) } } } dumpClipboard()原生侧的类型枚举管线EnumerateAvailableTypes(见 electron_api_clipboard.cc)解释了types数组是如何聚合出来的,它按顺序合并三个来源:
ReadAvailableStandardAndCustomFormatNames— 标准 MIME 类型及web前缀的 W3C 自定义格式;GetAllAvailableFormats— 其余所有原始平台格式。标准格式会被过滤掉(避免text/plain和electron application/osclipboard;format="public.utf8-plain-text"重复暴露同一条内容),其余包装为 osclipboard MIME;macOS 上还在此处探测 find pasteboard 并追加findtext伪 MIME;ReadURL(非 Linux)— 若剪贴板中有书签,则追加electron application/bookmark。
因此read()返回的types是一份完整的"聚合 MIME 列表",而每个类型的具体负载由getType(type)按需从平台剪贴板惰性读取。
clipboard.write(data)
dataClipboardItem[] — 通过new ClipboardItem({ [mime]: payload })构造的条目数组
返回Promise<void>。单次write()调用中的所有条目原子地提交到系统剪贴板:
const { clipboard, ClipboardItem, nativeImage } = require('electron') const png = nativeImage.createFromPath('/path/to/icon.png').toPNG() async function writeClipboard () { await clipboard.write([ new ClipboardItem({ 'text/plain': 'hello', 'text/html': '<b>hello</b>', 'image/png': new Blob([png], { type: 'image/png' }), 'electron application/bookmark': { title: 'Electron', url: 'https://electronjs.org' } }) ]) } writeClipboard()JS 封装层如何保证"先解析、后原子提交"可以清晰地从源码看出:lib/browser/api/clipboard.ts 中的wrapClipboard拦截write,先把每个ClipboardItem的Blob/Promise负载全部 resolve 成Buffer(kToNative),再经一次同步的原生write提交。而 C++ 侧Clipboard::Write(electron_api_clipboard.cc)用单个ui::ScopedClipboardWriter依次写入所有条目,析构时才真正提交——这就是原子性的实现来源。
两个值得注意的运行时约束(均可在测试与源码中验证):
write()参数必须是ClipboardItem数组,非数组或非ClipboardItem元素会抛出TypeError;clipboard.read()返回的ClipboardItem是"只读"的轻量读取器,不能回传给write(),必须重新构造新的ClipboardItem(见 lib/browser/api/clipboard-item.ts 中[kToNative]的显式拒绝逻辑)。
clipboard.has(mimetype)
mimetypestring - 要检查的 MIME 类型
返回Promise<boolean>,剪贴板中存在该 MIME 数据时 resolve 为true。要检查原始平台格式(如public/utf8-plain-text),需使用 osclipboard 自定义格式:
const { clipboard } = require('electron') async function check () { const hasFormat = await clipboard.has('text/html') console.log(hasFormat) // 'true' 或 'false' const rawFormat = 'electron application/osclipboard;format="public/utf8-plain-text"' const hasRawFormat = await clipboard.has(rawFormat) } check()实现上,has与read共用同一条聚合枚举管线(EnumerateAvailableTypes后做成员判定),所以has对read暴露的每一种 MIME——标准类型、web前缀格式、osclipboard 原始格式、bookmark、macOS findtext——都保持一致的判定结果。
clipboard.clear()
清除剪贴板内容(同步方法)。
安全边界:MIME 键即能力面
clipboard-item.md 文档中有一条重要警告:不要直接用不可信对象构造ClipboardItem(例如从渲染进程经 IPC 传来的负载)。MIME 键本身是能力面:text/uri-list会把真实文件引用放到 OS 剪贴板(允许粘贴文件到其他应用),electron application/osclipboard;format=...和web前缀格式会写入原始平台数据。从未经自己审核的数据构建ClipboardItem之前,务必对 MIME 类型和负载结构做白名单校验。
Linux 特有:clipboard.selection属性
在 Linux 上还存在一个selection剪贴板(对应 X11 的 PRIMARY 选择),通过clipboard.selection子命名空间暴露,它与顶层clipboard接口完全同构:
const { clipboard } = require('electron') async function run () { await clipboard.selection.writeText('Example string') console.log(await clipboard.selection.readText()) } run()两个剪贴板相互独立:通过clipboard.selection写入的数据不会影响clipboard.read()的返回值(反之亦然)。注意selection剪贴板不支持W3C web 自定义格式。
[!NOTE]
clipboard.selection是只读属性:Linux 上为一个Clipboard对象,暴露与顶层一致的read、write、readText、writeText、has、clear方法;其他平台为undefined。
从源码结构看,electron_api_clipboard.cc 中的Initialize在 Linux 分支上调用两次PopulateClipboardObject——分别绑定kCopyPaste和kSelection两个ui::ClipboardBuffer——同一套 C++ 方法通过绑定期注入不同的 buffer 实例化为两个 JS 对象。JS 层(lib/browser/api/clipboard.ts)仅在binding.selection存在时才挂selection包装对象。
测试视角的行为验证
仓库中的 spec/api-clipboard-spec.ts 对上述文档声明提供了逐项的行为验证,可作为可信行为参考:
image/*负载通过clipboard.write+getType完成NativeImage往返(写入后读回的数据 URL 一致);- 剪贴板只有文本时,
read()的types中不出现任何 image 类型; readText()/writeText()均返回Promise,且 Unicode 文本(如千江有水千江月,万里无云万里天)正确往返;has()对已写入的标准 MIME、web前缀自定义 MIME(如web text/plain+electron-test)、以及 osclipboard 原始格式(public/utf8-plain-text)均能正确返回true,未写入时返回false。
小结
| 能力 | API | 关键约束 |
|---|---|---|
| 纯文本读写 | readText()/writeText(text) | 返回 Promise;Windows 自动回退 ASCII 读取 |
| 多格式原子写入 | write(ClipboardItem[]) | 所有条目单次原子提交;不能接受read()产生的读取器 |
| 全量读取 | read() | 返回单元素ClipboardItem[],types为聚合 MIME 列表,负载惰性读取 |
| 存在性检查 | has(mimetype) | 与read()的暴露面严格一致;原始格式需用 osclipboard MIME |
| 清空 | clear() | 同步 |
| Linux 选择剪贴板 | clipboard.selection.* | 与系统剪贴板隔离;不支持 web 自定义格式 |
这套 API 的实用价值在于:主进程既能以 W3C 标准形态操作常见文本/HTML/图片/书签/文件列表,又能通过electron application/osclipboard;format="..."穿透到 Windows 注册格式、macOS pasteboard 类型等任意平台格式,实现与其他桌面应用的数据互操作——这正是"以 W3C Clipboard API 为骨架、以平台原始格式为逃生舱"的完整落地。
【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考