异步clipboard api
Posted 玛德致
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异步clipboard api相关的知识,希望对你有一定的参考价值。
原文地址:https://webkit.org/blog/10855/async-clipboard-api/;
第一次翻译,自觉语句什么的也有些生硬,可能存在不少问题,请大家不吝赐教,谢谢。
1.基础功能
Safari13.1 增加对async Clipboard API的支持。该api允许web开发者在剪贴板中读写文字/图片数据,并且在剪贴板数据的读写中有很多优于当前的做法。
该api主要介绍了两个功能:
- clipboard:通过navigator.clipboard使用,包含了一些从系统剪贴板读写数据的方法;
- clipboardItem:代表了剪贴板众多信息中的一项,目前webkit读写支持四种MIME类型:text/plain,text/html,text/uri-list和image/png;
从概念上来说,clipboard由一个或多个clipboardItem有序组成。每个clipboardItem可能会包含多种MIME类型,比如说复制多个image文件,一般会创建多个“image/png”类型的clipboardItem,但是如果图文混合类型,一般会创建一个带有“image/png”和“text/plain”两种类型的clipboardItem。
可以使用clipboard.read去读取系统剪切板中的数据,这个方法可以异步的获取到clipboardItem的数组集合,数组中的每一项都包含了MIME类型到blob的映射。同样的,clipboard.write可以将给定的clipboardItem数组写入系统剪切板。但如果内容只是文本的话,使用clipboard.readText和clipboard.writeText更简单方便些。
每个clipboardItem还有presentationStyle属性,该属性表明该项最好是内联数据还是“附件”(类似file的实体),可以用于区分文本还是HTML文件。
2.代码示例
写入文本数据:
<button id="new-copy">Copy text</button>
<script>
document.getElementById("new-copy").addEventListener("click", event => {
navigator.clipboard.writeText("This text was copied programmatically.");
});
</script>
这比现在需要选中text内容然后再调用execute复制文本要简单很多。
写入clipboardItem:
参数是一个clipboardItem数组,每一项MIME类型都是promise,resolve的内容可以被转为相同类型的string或者blob
<button id="copy-html">Copy text and markup</button>
<div>Then paste in the box below:</div>
<div contenteditable spellcheck="false" style="width: 200px; height: 100px; overflow: hidden; border: 1px solid black;"></div>
<script>
document.getElementById("copy-html").addEventListener("click", event => {
navigator.clipboard.write([
new ClipboardItem({
"text/plain": Promise.resolve("This text was copied using `Clipboard.prototype.write`."),
"text/html": Promise.resolve("<p style=\'color: red; font-style: oblique;\'>This text was copied using <code>Clipboard.prototype.write</code>.</p>"),
}),
]);
});
</script>
使用dataTransfer api来复制内容的时候,需要创建一个隐藏的文本域,在文本域上注册一个copy的事件,聚焦之后通过触发程序的复制在dataTransfer里设置数据,最后调用preventDefault方法。
write和writeItem都是异步的,如果在pending的时候重新写入,前一个会立即返回reject,然后写入新的内容。
在ios和macOS系统上,写入的顺序也很重要。webkit按照指定的顺序写入,先写入的有“更高的保真度”,在IOS和macOS上的原生app可以以此为根据来选择适当的UTI(universal type identifier)来读取。
读取数据:
<span style="font-weight: bold; background-color: black; color: white;">Select this text and copy</span>
<div><button id="read-html">Paste HTML below</button></div>
<div id="html-output"></div>
<script>
document.getElementById("read-html").addEventListener("click", async clickEvent => {
let items = await navigator.clipboard.read();
for (let item of items) {
if (!item.types.includes("text/html"))
continue;
let reader = new FileReader;
reader.addEventListener("load", loadEvent => {
document.getElementById("html-output").innerHTML = reader.result;
});
reader.readAsText(await item.getType("text/html"));
break;
}
});
</script>
有几个点可以注意一下:
- 和write一样,read也是异步的,获取clipboardItem和转换blob都返回的是promise
- 读取数据时,类型会保留,写入的顺序和读取的时候是一样的
3.安全和隐私
clipboard api功能强大,可以从剪切板读取数据,所以读取时有很多安全策略,敏感数据,比如密码之类的,如果没有用户 明确的同意,是不被允许使用的。比如说复制了恶意的“text/html”信息,粘贴到另外的网页中,很有可能导致跨站脚本攻击。
以下是一些安全限制:
- http的网站是无法使用该api的
- 复制的请求必须在用户手势下触发,如果不是“click”,“touch”之类的用户手势,promise会返回reject
- “text/html”和“image/png”在写入之前会脱敏,标记在js被禁用的独立文档中加载,而且只取页面的可见部分,比如script元素,注释内容,display:none以及事件属性等都会被去除。对png图片来说,被初次解码后展示图像,在被写入粘贴板之前会被再次编码,这得确保网站图片不被损坏,如果图片无法被解码,写入的promise会返回reject。更多的脱敏信息可以在Clipboard API Improvements中找到。
- 由于用户可能并没有意识到复制了敏感信息,所以读取粘贴内容的限制比写入的限制严格很多。如果页面企图在用户手势之外用编程方式读取粘贴板的内容,会立即返回reject。但是如果用户明确的触发了粘贴操作(比如macOS的command+v快捷键或者IOS的粘贴操作),WebKit会允许程序执行粘贴操作。如果是同源访问,剪切板会自动授权。如果上述条件都不符合,WebKit就会在用户继续粘贴操作的时候展示明确的ui面板让其操作。在IOS上,它的形式是有单一选项的标注栏,在macOS上,是菜单选项。轻触或点击页面的任意一处(或者执行其他操作,比如切换tab或者隐藏safari)都会导致promise被reject。只有用户在ui上明确触发了粘贴操作,页面才会授权允许操作执行。
- 从剪贴板读取数据也要进行脱敏,避免用户在无意中暴露敏感数据,比如image数据会被剥离EXIF数据,其中包含了名字之类的信息。读取的标记信息也会被去掉注释之类的信息。
这些政策确保了异步clipboard api开发者没有滥用以及破坏用户安全与隐私的风险,还能获得良好的开发体验。
4.未来的发展
我们会继续完善这个api,我们会增加自定义类型的支持,也会支持更多的MIME类型,比如“image/jpeg”或者“image/svg+xml”,如果有bug欢迎提供至bugs.webkit.org
以上是关于异步clipboard api的主要内容,如果未能解决你的问题,请参考以下文章
Web API 学习笔记 - 剪切板 Clipboard API