Electron clipboard 模块实战指南:基于 W3C Clipboard API 的系统剪贴板操作与自定义 MIME 格式
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 注释):在渲染进程中直接使用 clipboard API 已被废弃,该模块只在主进程中可用,进程模型定义参见 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>,以纯文本形式读取剪贴板内容,对标 W3C navigator.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,对标 W3C navigator.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 为骨架、以平台原始格式为逃生舱"的完整落地。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00