
如果你使用電腦已有一段時間,你可能知道剪貼板可以存儲多種類型的數(shù)據(jù)(圖像、富文本內(nèi)容、文件等)。作為一名軟件開發(fā)人員,我開始感到困擾,因為我對剪貼板如何存儲和組織不同類型的數(shù)據(jù)沒有很好的理解。
最近,我決定揭開剪貼板的神秘面紗,并根據(jù)我的學習撰寫了這篇文章。我們將重點關注網(wǎng)頁剪貼板及其API,但也會簡要討論它與操作系統(tǒng)剪貼板的交互。
我們將從探索網(wǎng)頁的剪貼板API及其歷史開始。剪貼板API在數(shù)據(jù)類型方面有一些有趣的限制,我們將看到一些公司如何繞過這些限制。我們還將看看一些旨在解決這些限制的提案(最顯著的是 Web Custom Formats)。
如果你曾經(jīng)對網(wǎng)頁剪貼板的工作原理感到好奇,這篇文章適合你。
使用異步Clipboard API
如果我從一個網(wǎng)站復制一些內(nèi)容并粘貼到Google Docs中,一些格式會保留,例如鏈接、字體大小和顏色。

但是,如果我打開VS Code并粘貼內(nèi)容,只有純文本被粘貼。

剪貼板通過允許以與MIME類型關聯(lián)的多種表示形式存儲信息來服務這兩種用例。W3C剪貼板規(guī)范規(guī)定,在向剪貼板寫入和從剪貼板讀取時,必須支持以下三種數(shù)據(jù)類型:
-
text/plain用于純文本。 -
text/html用于HTML。 -
image/png用于PNG圖像。
因此,當我粘貼到Google Docs時,它讀取了 text/html 表示形式,并使用它保留了富文本格式。而VS Code只關心原始文本,并讀取了 text/plain 表示形式。這很合理。
通過異步Clipboard API的 read 方法讀取特定表示形式非常簡單:
const items = await navigator.clipboard.read();
for (const item of items) {
if (item.types.includes("text/html")) {
const blob = await item.getType("text/html");
const html = await blob.text();
// 對HTML進行處理...
}
}
通過 write 將多個表示形式寫入剪貼板稍微復雜一些,但仍然相對簡單。首先,我們?yōu)槊總€要寫入剪貼板的表示形式構建 Blob:
const textBlob = new Blob(["Hello, world"], { type: "text/plain" });
const htmlBlob = new Blob(["Hello, <em>world<em>"], { type: "text/html" });
一旦我們有了 Blob,我們將它們傳遞給一個新的 ClipboardItem,并以數(shù)據(jù)類型為鍵、Blob 為值的鍵值對存儲它們:
const clipboardItem = new ClipboardItem({
[textBlob.type]: textBlob,
[htmlBlob.type]: htmlBlob,
});
注意:我喜歡
ClipboardItem接受鍵值存儲的方式。它很好地符合使用數(shù)據(jù)結構避免非法狀態(tài)表示的理念,就像在 "Parse, don't validate" 中討論的那樣。
最后,我們調(diào)用 write,傳入我們新構建的 ClipboardItem:
await navigator.clipboard.write([clipboardItem]);
其他數(shù)據(jù)類型呢?
HTML和圖像很酷,但像JSON這樣的通用數(shù)據(jù)交換格式呢?如果我在編寫支持復制粘貼的應用程序,我可以想象希望將JSON或某些二進制數(shù)據(jù)寫入剪貼板。
讓我們嘗試將JSON數(shù)據(jù)寫入剪貼板:
// 創(chuàng)建JSON blob
const json = JSON.stringify({ message: "Hello" });
const blob = new Blob([json], { type: "application/json" });
// 將JSON blob寫入剪貼板
const clipboardItem = new ClipboardItem({ [blob.type]: blob });
await navigator.clipboard.write([clipboardItem]);
運行后,會拋出一個異常:
無法在 'Clipboard' 上執(zhí)行 'write':
不支持 application/json 類型的寫入。
嗯,這是什么情況?根據(jù) write 規(guī)范,除了 text/plain、text/html 和 image/png 之外的其他數(shù)據(jù)類型必須被拒絕:
如果類型不在強制性數(shù)據(jù)類型列表中,則拒絕 [...] 并中止這些步驟。
有趣的是,application/json MIME類型曾經(jīng)在2012年到2021年的強制數(shù)據(jù)類型列表中,但在 w3c/clipboard-apis#155 中被移除。在此之前,讀取剪貼板的強制性數(shù)據(jù)類型有16種,寫入剪貼板的有8種。變更之后,只剩下 text/plain、text/html 和 image/png。
此變更是由于瀏覽器出于安全考慮而選擇不支持許多強制性類型導致的。這一點在規(guī)范中的強制數(shù)據(jù)類型部分通過警告得到了反映。
警告!作為安全預防措施,不受信任的腳本只能將有限的數(shù)據(jù)類型寫入剪貼板。
不受信任的腳本可能會試圖通過在剪貼板上放置已知會觸發(fā)漏洞的數(shù)據(jù)來利用本地軟件中的安全漏洞。
好的,所以我們只能將有限的數(shù)據(jù)類型寫入剪貼板。但是,關于“不受信任的腳本”是怎么回事呢?我們能否以某種方式運行“受信任”的腳本,允許我們將其他數(shù)據(jù)類型寫入剪貼板?
isTrusted 屬性
也許“受信任”的部分指的是事件的 isTrusted 屬性。isTrusted 是一個只讀屬性,僅當事件由用戶代理分發(fā)時,其值才會被設置為 true。
document.addEventListener("copy", (e) => {
if (e.isTrusted) {
// 該事件由用戶代理觸發(fā)
}
});
“由用戶代理分發(fā)”意味著它是由用戶觸發(fā)的,例如用戶按下 Command + C 時觸發(fā)的復制事件。這與通過 dispatchEvent() 程序化調(diào)度的合成事件形成對比:
document.addEventListener("copy", (e) => {
console.log("e.isTrusted is " + e.isTrusted);
});
document.dispatchEvent(new ClipboardEvent("copy"));
// => "e.isTrusted is false"
讓我們看看剪貼板事件,并看看它們是否允許我們將任意數(shù)據(jù)類型寫入剪貼板。
剪貼板事件 API
當發(fā)生復制、剪切和粘貼事件時,會調(diào)度一個 ClipboardEvent,該事件包含一個類型為 DataTransfer 的 clipboardData 屬性。DataTransfer 對象由剪貼板事件 API 使用,用于保存多種數(shù)據(jù)表示形式。
在復制事件中寫入剪貼板非常簡單:
document.addEventListener("copy", (e) => {
e.preventDefault(); // 阻止默認的復制行為
e.clipboardData.setData("text/plain", "Hello, world");
e.clipboardData.setData("text/html", "Hello, <em>world</em>");
});
在粘貼事件中讀取剪貼板內(nèi)容同樣很簡單:
document.addEventListener("paste", (e) => {
e.preventDefault(); // 阻止默認的粘貼行為
const html = e.clipboardData.getData("text/html");
if (html) {
// 對HTML進行處理...
}
});
現(xiàn)在,大問題來了:我們能將JSON寫入剪貼板嗎?
document.addEventListener("copy", (e) => {
e.preventDefault();
const json = JSON.stringify({ message: "Hello" });
e.clipboardData.setData("application/json", json); // 沒有錯誤
});
沒有拋出異常,但這真的將JSON寫入了剪貼板嗎?讓我們通過編寫一個粘貼處理程序,遍歷剪貼板中的所有條目并將其打印出來來驗證:
document.addEventListener("paste", (e) => {
for (const item of e.clipboardData.items) {
const { kind, type } = item;
if (kind === "string") {
item.getAsString((content) => {
console.log({ type, content });
});
}
}
});
添加這些處理程序并執(zhí)行復制粘貼后,會記錄如下內(nèi)容:
{ "type": "application/json", "content": "{\"message\":\"Hello\"}" }
成功了!看來 clipboardData.setData 并不像異步 write 方法那樣對數(shù)據(jù)類型進行限制。
但為什么呢?為什么我們可以使用 clipboardData 讀寫任意數(shù)據(jù)類型,而使用異步剪貼板API時卻不行?
clipboardData 的歷史
相對較新的異步剪貼板API是在2017年添加到規(guī)范中的,而 clipboardData 早在那之前就已經(jīng)存在。2006年關于剪貼板API的W3C草案定義了 clipboardData 及其 setData 和 getData 方法(這表明當時MIME類型還未被使用):
-
setData()接受一個或兩個參數(shù)。第一個必須設置為 'text' 或 'URL'(不區(qū)分大小寫)。 -
getData()接受一個參數(shù),允許目標請求特定類型的數(shù)據(jù)。
但是,clipboardData 實際上比2006年草案還要早。請看該文檔狀態(tài)部分的這段話:
該文檔大部分描述了在Internet Explorer中實現(xiàn)的功能...
這表明該文檔的目的是為了描述當前瀏覽器中實際可用的功能,以提高互操作性,而不是添加新功能。
一篇2003年的文章詳細介紹了在Internet Explorer 4及以上版本中,如何使用 clipboardData 在未經(jīng)用戶同意的情況下讀取用戶的剪貼板內(nèi)容。鑒于Internet Explorer 4發(fā)布于1997年,這表明 clipboardData 接口至少已經(jīng)有26年的歷史了。
MIME類型在2011年進入規(guī)范:
dataType參數(shù)是一個字符串,例如但不限于MIME類型...
如果腳本調(diào)用
getData('text/html')...
當時,規(guī)范還沒有確定應該使用哪些數(shù)據(jù)類型:
雖然可以為
setData()的類型參數(shù)使用任意字符串,但建議堅持使用常見類型。
今天,setData 和 getData 仍然可以使用任意字符串。這完全沒有問題:
document.addEventListener("copy", (e) => {
e.preventDefault();
e.clipboardData.setData("foo bar baz", "Hello, world");
});
document.addEventListener("paste", (e) => {
const content = e.clipboardData.getData("foo bar baz");
if (content) {
console.log(content); // 輸出 "Hello, world!"
}
});
如果你將這個代碼片段粘貼到你的開發(fā)者工具中,然后執(zhí)行復制粘貼操作,你會看到控制臺輸出“Hello, world!”。
剪貼板事件API的 clipboardData 允許我們使用任意數(shù)據(jù)類型的原因似乎是歷史原因:“不要破壞網(wǎng)絡”。
再次探討 isTrusted
讓我們重新考慮一下強制性數(shù)據(jù)類型部分的這句話:
作為安全預防措施,不受信任的腳本只能將有限的數(shù)據(jù)類型寫入剪貼板。
那么,如果我們在合成(不受信任的)剪貼板事件中嘗試寫入剪貼板會發(fā)生什么?
document.addEventListener("copy", (e) => {
e.preventDefault();
e.clipboardData.setData("text/plain", "Hello");
});
document.dispatchEvent(new ClipboardEvent("copy", {
clipboardData: new DataTransfer(),
}));
這段代碼成功運行,但它并沒有修改剪貼板。這是預期的行為,正如規(guī)范中所解釋的:
合成的剪切和復制事件不得修改系統(tǒng)剪貼板上的數(shù)據(jù)。
合成的粘貼事件不得給予腳本訪問真實系統(tǒng)剪貼板數(shù)據(jù)的權限。
因此,只有由用戶代理分發(fā)的復制和粘貼事件才能修改剪貼板。這完全合理——我不希望網(wǎng)站可以隨意讀取我的剪貼板內(nèi)容并竊取我的密碼。
- 引入于2017年的異步剪貼板API限制了可以寫入和讀取剪貼板的數(shù)據(jù)類型。然而,只要用戶授予權限(且文檔處于焦點狀態(tài)),它可以隨時讀取和寫入剪貼板。
- 較舊的剪貼板事件API對寫入和讀取剪貼板的數(shù)據(jù)類型沒有實質(zhì)性的限制。然而,它只能在由用戶代理觸發(fā)的復制和粘貼事件處理程序中使用(即當
isTrusted為true時)。
如果你想向剪貼板寫入不僅僅是純文本、HTML或圖像的數(shù)據(jù)類型,那么使用剪貼板事件API似乎是唯一的方法。在這方面限制要少得多。
但是,如果您想構建一個復制按鈕,將非標準數(shù)據(jù)類型寫入剪貼板,該怎么辦?如果用戶沒有觸發(fā)復制事件,您似乎無法使用剪貼板事件API。對嗎?”
構建一個寫入任意數(shù)據(jù)類型的復制按鈕

這個復制按鈕將三種表示形式寫入剪貼板:
text/plaintext/htmlapplication/x-vnd.google-docs-document-slice-clip+wrapped
注意:第三種表示形式包含 JSON 數(shù)據(jù)。
他們將自定義數(shù)據(jù)類型寫入剪貼板,這意味著他們沒有使用異步剪貼板 API。那么他們是如何通過點擊處理程序做到這一點的呢?
我運行了分析器,點擊了復制按鈕,并檢查了結果。事實證明,點擊復制按鈕會觸發(fā) document.execCommand("copy") 的調(diào)用。

這讓我感到驚訝。我的第一個想法是:“execCommand 不是舊的、已棄用的復制文本到剪貼板的方法嗎?”
是的,它是,但谷歌這樣使用是有原因的。execCommand 的特殊之處在于,它允許你以編程方式調(diào)度一個受信任的復制事件,就像用戶自己執(zhí)行了復制命令一樣。
document.addEventListener("copy", (e) => {
console.log("e.isTrusted is " + e.isTrusted);
});
document.execCommand("copy");
// => "e.isTrusted is true"
注意:Safari 需要一個有效的選區(qū)來讓 execCommand("copy") 調(diào)度復制事件。可以通過向 DOM 添加一個非空的輸入元素并在調(diào)用 execCommand("copy") 之前選擇它來偽造該選區(qū),然后再將該輸入元素從 DOM 中移除。
好的,所以使用 execCommand 可以讓我們在響應點擊事件時將任意數(shù)據(jù)類型寫入剪貼板。很酷!
那么粘貼呢?我們可以使用 execCommand("paste") 嗎?
構建一個粘貼按鈕
讓我們試一下 Google Docs 中的粘貼按鈕,看看它做了什么。

在我的 Macbook 上,我收到一個彈窗,提示我需要安裝一個擴展程序才能使用粘貼按鈕。

但奇怪的是,在我的 Windows 筆記本電腦上,粘貼按鈕卻可以正常工作。
很奇怪。這種不一致性來自哪里?嗯,是否粘貼按鈕能夠正常工作,可以通過運行 queryCommandSupported("paste") 檢查:
document.queryCommandSupported("paste");
在我的 Macbook 上,我在 Chrome 和 Firefox 上得到了 false,但在 Safari 上得到了 true。
Safari 出于隱私考慮,要求我確認粘貼操作。我認為這是一個非常好的主意。它明確表示該網(wǎng)站將從你的剪貼板中讀取數(shù)據(jù)。

在我的 Windows 筆記本電腦上,我在 Chrome 和 Edge 上得到了 true,但在 Firefox 上得到了 false。Chrome 的這種不一致性讓人感到驚訝。為什么 Chrome 在 Windows 上允許 execCommand("paste"),但在 macOS 上不允許?對此我找不到任何相關信息。
讓我感到意外的是,谷歌并沒有在 execCommand("paste") 不可用時回退到異步剪貼板 API。雖然他們無法使用它讀取 application/x-vnd.google-[...] 表示形式,但 HTML 表示形式包含內(nèi)部 ID,可以使用這些 ID。
<!-- 清理后的 HTML 表示形式 -->
<meta charset="utf-8">
<b id="docs-internal-guid-[guid]" style="...">
<span style="...">復制的文本</span>
</b>
另一個具有粘貼按鈕的 Web 應用程序是 Figma,他們采取了完全不同的方式。讓我們看看他們在做什么。
Figma 中的復制和粘貼
Figma 是一個基于 Web 的應用程序(他們的原生應用使用 Electron)。讓我們看看他們的復制按鈕將什么寫入剪貼板。

Figma 的復制按鈕將兩種表示形式寫入剪貼板:
text/plain 和 text/html。一開始這讓我感到驚訝。Figma 如何用普通 HTML 表示他們的各種布局和樣式功能呢?
但查看 HTML 后,我們看到兩個帶有 data-metadata 和 data-buffer 屬性的空 span 元素:
<meta charset="utf-8">
<div>
<span data-metadata="<!--(figmeta)eyJma[...]9ifQo=(/figmeta)-->"></span>
<span data-buffer="<!--(figma)ZmlnL[...]P/Ag==(/figma)-->"></span>
</div>
<span style="white-space:pre-wrap;">文本</span>
注意:data-buffer 字符串對于一個空框架大約有 26,000 個字符。在此之后,data-buffer 的長度似乎隨著復制的內(nèi)容量線性增長。
看起來像是 base64 編碼。eyJ 開頭的部分清楚地表明 data-metadata 是一個 base64 編碼的 JSON 字符串。對 data-metadata 運行 JSON.parse(atob()) 后得到:
{
"fileKey": "4XvKUK38NtRPZASgUJiZ87",
"pasteID": 1261442360,
"dataType": "scene"
}
注意:我替換了真實的 fileKey 和 pasteID。
那么龐大的 data-buffer 屬性呢?對其進行 base64 解碼后得到如下內(nèi)容:
fig-kiwiF\x00\x00\x00\x1CK\x00\x00μ?\v\x9CdI[...]\x197ü\x83\x03
看起來像是一個二進制格式。經(jīng)過一番調(diào)查——利用“fig-kiwi”這個線索——我發(fā)現(xiàn)這是 Kiwi 消息格式(由 Figma 的聯(lián)合創(chuàng)始人兼前 CTO Evan Wallace 創(chuàng)建),用于編碼 .fig 文件。
由于 Kiwi 是一個基于模式的格式,沒有模式我們似乎無法解析這些數(shù)據(jù)。不過,幸運的是,Evan 創(chuàng)建了一個公開的 .fig 文件解析器。讓我們嘗試將緩沖區(qū)插入其中!
為了將緩沖區(qū)轉換為 .fig 文件,我編寫了一個小腳本來生成一個 Blob URL:
const base64 = "ZmlnL[...]P/Ag==";
const blob = base64toBlob(base64, "application/octet-stream");
console.log(URL.createObjectURL(blob));
// => blob:<origin>/1fdf7c0a-5b56-4cb5-b7c0-fb665122b2ab
然后我下載了生成的 Blob 作為 .fig 文件,并將其上傳到 .fig 文件解析器,結果就是這樣:

因此,F(xiàn)igma 的復制操作實際上是通過創(chuàng)建一個小型的 Figma 文件,將其編碼為 base64,然后將生成的 base64 字符串放入空 HTML
span 元素的 data-buffer 屬性中,并將其存儲在用戶的剪貼板中。
復制粘貼 HTML 的好處
一開始這看起來有些可笑,但這種方法有一個很大的好處。為了理解這個好處,先看看 Web 剪貼板 API 如何與各種操作系統(tǒng)的剪貼板 API 交互。
Windows、macOS 和 Linux 都為將數(shù)據(jù)寫入剪貼板提供了不同的格式。如果你想將 HTML 寫入剪貼板,Windows 有 CF_HTML,而 macOS 有 NSPasteboard.PasteboardType.html。
所有操作系統(tǒng)都提供“標準”格式(純文本、HTML 和 PNG 圖像)的類型。但是,當用戶嘗試將 application/foo-bar 這樣的任意數(shù)據(jù)類型寫入剪貼板時,瀏覽器應該使用哪種操作系統(tǒng)格式呢?
沒有一個好的匹配項,所以瀏覽器不會將該表示寫入操作系統(tǒng)剪貼板中的常見格式。相反,該表示只存在于操作系統(tǒng)剪貼板上的自定義瀏覽器特定格式中。這導致能夠跨瀏覽器標簽復制和粘貼任意數(shù)據(jù)類型,但不能跨應用程序使用。
這就是為什么使用常見的數(shù)據(jù)類型 text/plain、text/html 和 image/png 如此方便。它們映射到操作系統(tǒng)剪貼板的常見格式,因此可以很容易地被其他應用程序讀取,使復制/粘貼能夠在不同的應用程序之間正常工作。對于 Figma 來說,使用 text/html 使得可以在瀏覽器中的 figma.com 復制一個 Figma 元素,然后將其粘貼到本地 Figma 應用程序中,反之亦然。
瀏覽器將什么寫入剪貼板作為自定義數(shù)據(jù)類型?
我們了解到可以跨瀏覽器標簽向剪貼板寫入和讀取自定義數(shù)據(jù)類型,但不能跨應用程序使用。但是,當我們將自定義數(shù)據(jù)類型寫入 Web 剪貼板時,瀏覽器究竟將什么寫入本地操作系統(tǒng)剪貼板呢?
我在我的 Macbook 上的每個主要瀏覽器中運行了以下代碼,設置了一個復制監(jiān)聽器:
document.addEventListener("copy", (e) => {
e.preventDefault();
e.clipboardData.setData("text/plain", "Hello, world");
e.clipboardData.setData("text/html", "<em>Hello, world</em>");
e.clipboardData.setData("application/json", JSON.stringify({ type: "Hello, world" }));
e.clipboardData.setData("foo bar baz", "Hello, world");
});
然后我使用 Pasteboard Viewer 檢查剪貼板內(nèi)容。Chrome 向剪貼板添加了四個條目:
-
public.html包含 HTML 表示。 -
public.utf8-plain-text包含純文本表示。 -
org.chromium.web-custom-data包含自定義表示。 -
org.chromium.source-url包含執(zhí)行復制操作的網(wǎng)頁 URL。
查看 org.chromium.web-custom-data,我們看到我們復制的數(shù)據(jù):

Firefox 也創(chuàng)建了 public.html 和 public.utf8-plain-text 條目,但將自定義數(shù)據(jù)寫入 org.mozilla.custom-clipdata。它不像 Chrome 那樣存儲源 URL。
Safari(如你所料)也創(chuàng)建了 public.html 和 public.utf8-plain-text 條目。它將自定義數(shù)據(jù)寫入 com.apple.WebKit.custom-pasteboard-data,并且有趣的是,它還在那里存儲了完整的表示形式列表(包括純文本和 HTML)以及源 URL。
注意:Safari 允許跨瀏覽器標簽復制粘貼自定義數(shù)據(jù)類型,如果源 URL(域名)相同,但不能跨不同域名。這種限制似乎在 Chrome 或 Firefox 中不存在(盡管 Chrome 存儲源 URL)。
Web 原始剪貼板訪問
2019 年提出了一個原始剪貼板訪問的提案,提出了一個 API,允許 Web 應用程序對本地操作系統(tǒng)剪貼板進行原始讀寫訪問。
這個摘錄來自 chromestatus.com 上的原始剪貼板訪問功能的動機部分,簡潔地突出了它的好處:
“沒有原始剪貼板訪問 [...] Web 應用程序通常只能訪問少數(shù)格式,無法與大多數(shù)格式互操作。例如,F(xiàn)igma 和 Photopea 無法與大多數(shù)圖像格式互操作?!?/p>
然而,由于安全問題(例如在本地應用程序中遠程代碼執(zhí)行),原始剪貼板訪問提案最終沒有進一步推進。
最新的自定義數(shù)據(jù)類型寫入剪貼板的提案是 Web 自定義格式提案(通常稱為 pickling)。
Web 自定義格式(Pickling)
2022 年,Chromium 在異步剪貼板 API 中實現(xiàn)了對 Web 自定義格式的支持。
它允許 Web 應用程序通過異步剪貼板 API 寫入自定義數(shù)據(jù)類型,方法是為數(shù)據(jù)類型添加前綴“web”:
// 創(chuàng)建 JSON Blob
const json = JSON.stringify({ message: "Hello, world" });
const jsonBlob = new Blob([json], { type: "application/json" });
// 將 JSON Blob 寫入剪貼板作為 Web 自定義格式
const clipboardItem = new ClipboardItem({
[`web ${jsonBlob.type}`]: jsonBlob,
});
navigator.clipboard.write([clipboardItem]);
這些數(shù)據(jù)可以像其他數(shù)據(jù)類型一樣使用異步剪貼板 API 讀?。?/p>
const items = await navigator.clipboard.read();
for (const item of items) {
if (item.types.includes("web application/json")) {
const blob = await item.getType("web application/json");
const json = await blob.text();
// 處理 JSON 數(shù)據(jù)...
}
}
更有趣的是,寫入本地剪貼板時會發(fā)生什么。當寫入 Web 自定義格式時,以下內(nèi)容會被寫入本地操作系統(tǒng)剪貼板:
- 數(shù)據(jù)類型到剪貼板條目名稱的映射
- 每種數(shù)據(jù)類型的剪貼板條目
在 macOS 上,映射被寫入 org.w3.web-custom-format.map,其內(nèi)容如下:
{
"application/json": "org.w3.web-custom-format.type-0",
"application/octet-stream": "org.w3.web-custom-format.type-1"
}
org.w3.web-custom-format.type-[index] 鍵對應操作系統(tǒng)剪貼板中的條目,包含來自 Blob 的未經(jīng)處理的數(shù)據(jù)。這使本地應用程序可以查看映射,以確定是否有可用的表示形式,然后從相應的剪貼板條目中讀取未經(jīng)處理的內(nèi)容。
注意:Windows 和 Linux 使用不同的命名約定來表示映射和剪貼板條目。
這避免了圍繞原始剪貼板訪問的安全問題,因為 Web 應用程序無法將未經(jīng)處理的數(shù)據(jù)寫入它們想要的任何操作系統(tǒng)剪貼板格式。這伴隨著一種明確列在異步剪貼板 API 規(guī)范中的互操作性權衡:
非目標:允許與不更新的舊版本地應用程序進行互操作。這個問題在原始剪貼板提案中被探索過,可能會在將來進一步探索,但它帶來了重大安全挑戰(zhàn)(系統(tǒng)本地應用程序中的遠程代碼執(zhí)行)。
這意味著本地應用程序需要更新才能與 Web 應用程序在使用自定義數(shù)據(jù)類型時進行剪貼板互操作。
Web 自定義格式自 2022 年起在基于 Chromium 的瀏覽器中可用,但其他瀏覽器尚未實現(xiàn)該提案。
最后的話
截至目前,還沒有一種能夠跨所有瀏覽器寫入自定義數(shù)據(jù)類型的理想方式。Figma 將 base64 字符串放入 HTML 表示的方式雖然粗糙,但有效,它繞過了剪貼板 API 周圍的各種限制。如果你需要通過剪貼板傳輸自定義數(shù)據(jù)類型,這似乎是一種不錯的方法。