Image support for the async clipboard API

523 阅读6分钟
原文链接: s0web0dev.icopy.site

Chrome 66添加了对异步剪贴板API文本部分的支持. Chrome 76增加了对图像的支持,使通过编程方式复制和粘贴PNG图像更加容易.

注意:在撰写本文时,仅支持PNG文件. 将来会增加对其他图像和文件格式的支持.

Recap of the Asynchronous Clipboard API #

在描述图像支持之前,我想回顾一下异步剪贴板API的工作原理. 如果您已经习惯了,请随时跳过 .

Copy: writing text to the clipboard #

要将文本复制到剪贴板,请调用navigator.clipboard.writeText() . 由于此API是异步的,因此writeText()函数将返回一个承诺,该承诺将根据传递的文本是否被成功复制来解析或拒绝. 例如:

async function copyPageURL() {
  try {
    await navigator.clipboard.writeText(location.href);
    console.log('Page URL copied to clipboard');
  } catch (err) {
    console.error('Failed to copy: ', err);
  }
}

Paste: reading text from the clipboard #

类似于复制的功能,调用navigator.clipboard.readText()返回使用文本解析的promise:

async function getClipboardText() {
  try {
    let text = await navigator.clipboard.readText();
    console.log('Clipboard contents: ', text);
  } catch (err) {
    console.error('Failed to read clipboard contents: ', err);
  }
}

Handling paste events #

通过侦听(惊奇) paste事件来处理paste事件. 请注意,您需要调用preventDefault()来修改要粘贴的数据,例如在粘贴之前将其转换为大写. 它与用于读取剪贴板文本的新异步方法很好地配合使用:

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  try {
    let text = await navigator.clipboard.readText();
    text = text.toUpperCase();
    console.log('Pasted UPPERCASE text: ', text);
  } catch (err) {
    console.error('Failed to read clipboard contents: ', err);
  }
});

Security and permissions #

仅当页面是活动选项卡时,才在通过HTTPS服务的页面上允许使用navigator.clipboard属性,并有助于防止滥用. 活动选项卡中的页面可以在不请求权限的情况下写入剪贴板,但是从剪贴板中读取始终需要权限.

引入异步剪贴板API时,将两个新的复制和粘贴权限添加到Permissions API中

  • 处于活动选项卡中的页面会自动授予clipboard-write权限.
  • 必须请求clipboard-read权限,您可以通过尝试从剪贴板读取数据来完成.

下面的代码显示了后者:

const queryOpts = { name: 'clipboard-read' };
const permissionStatus = await navigator.permissions.query(queryOpts);
// Will be 'granted', 'denied' or 'prompt':
console.log(permissionStatus.state);

// Listen for changes to the permission state
permissionStatus.onchange = () => {
  console.log(permissionStatus.state);
};

Images in the Asynchronous Clipboard API #

Copy: writing an image to the clipboard #

新的navigator.clipboard.write()图像复制到剪贴板. 与writeText() ,它是异步的并且基于Promise. 实际上, writeText()只是通用write()方法的一种便捷方法.

To write an image to the clipboard, you need the image as a Blob. One way to do this is by requesting the image from an server by calling fetch() (or XMLHTTPRequest()). The response object returned by fetch() has a blob() method and XMLHTTPRequest() let's you set "blob" as the responseType.

出于各种原因,不希望或不可能致电服务器. 幸运的是,您还可以将图像写入画布,然后调用HTMLCanvasElement.toBlob() .

接下来,将ClipboardItem对象数组作为参数传递给write()方法. 目前,您一次只能传送一张图片,但我们计划在将来增加对多张图片的支持.

ClipboardItem接受一个对象,该对象具有图像的MIME类型作为键,而实际的blob作为值. 下面的示例代码显示了使用新的动态属性键语法的灵活方式. 从blob.type检索用作密钥的MIME类型. 这种方法确保您的代码将为将来的图像类型以及将来可能支持的其他MIME类型做好准备.

try {
  const imgURL = '/images/generic/file.png';
  const data = await fetch(imgURL);
  const blob = await data.blob();
  await navigator.clipboard.write([
    new ClipboardItem({
      [blob.type]: blob
    })
  ]);
  console.log('Image copied.');
} catch(err) {
  console.error(err.name, err.message);
}

Paste: reading an image from the clipboard #

从剪贴板读取数据的navigator.clipboard.read()方法也是异步的,并且基于Promise.

要从剪贴板读取图像,请获取ClipboardItem对象的列表,然后对其进行迭代. 由于所有内容都是异步的,因此请使用for...of迭代器,因为它可以很好地处理异步/等待代码.

每个ClipboardItem可以将其内容保留为不同的类型,因此您需要遍历类型列表,再次使用for...of循环. 对于每种类型,请以当前类型为参数调用getType()方法以获得对应的图像Blob . 和以前一样,此代码不与图像绑定,并且可以与其他将来的文件类型一起使用.

async function getClipboardContents() {
  try {
    const clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      for (const type of clipboardItem.types) {
        const blob = await clipboardItem.getType(type);
        console.log(URL.createObjectURL(blob));
      }
    }
  } catch (err) {
    console.error(err.name, err.message);
  }
}

Custom paste handler #

若要动态处理粘贴事件,请侦听paste事件,然后调用preventDefault()以防止默认行为因您自己的逻辑而异,然后从剪贴板中读取内容,以应用程序需要的任何方式对其进行处理. 在下面的示例中,我通过调用上面创建的函数来完成最后一步.

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  getClipboardContents();
});

Custom copy handler #

copy事件包括一个clipboardData属性,其中各项的格式已经正确,从而无需手动创建Blob. 和以前一样,不要忘记调用preventDefault() .

document.addEventListener('copy', async (e) => {
  e.preventDefault();
  try {
    for (const item of e.clipboardData.items) {
      await navigator.clipboard.write([
        new ClipboardItem({
          [blob.type]: blob
        })
      ]);
    }
    console.log('Image copied.');
  } catch(err) {
    console.error(err.name, err.message);
  }
});

Demo #

Security #

打开图像的异步剪贴板API会带来一些风险 ,需要仔细评估. 一项新的挑战是所谓的图像压缩炸弹 ,即看起来无害的图像文件,但是一旦解压缩,它们就会变得庞大. 甚至比大型映像更严重的是经过精心设计的恶意映像,旨在利用本机操作系统中的已知漏洞. 这就是为什么Chrome无法将图像直接复制到本机剪贴板的原因,以及为什么在Chrome中我们要求对图像进行转码.

因此,该规范还明确提及将代码转换作为缓解方法:

为了防止将恶意图像数据放置在剪贴板上,可以对图像数据进行转码以生成图像的安全版本.

W3C技术架构小组的审查中正在进行有关是否以及如何指定转码详细信息的讨论.

Next Steps #

Chrome浏览器正在积极扩展异步剪贴板API,以增加对更多数据类型的支持. 由于潜在的风险,Chrome会谨慎操作. 为了及时了解Chrome的进度,您可以为该错误加注星标,以通知有关更改.

目前,可以在Chrome 76或更高版本中使用图片支持.

祝您复制和粘贴愉快!

Acknowledgements #

异步剪贴板API由Darwin HuangGaryKačmarčík实现 . 达尔文还提供了演示 . 我对本文的介绍受到Jason Miller原始 文本的启发. 感谢Kyarik,并再次感谢GaryKačmarčík审阅了本文.

Capabilities Fugu 最近更新时间: Improve article