使用文件

您的浏览器扩展程序可能需要使用文件才能提供其全部功能。本文将介绍处理文件的五种机制。

  • 将文件下载到用户选择的下载文件夹。
  • 使用网页上的文件选择器打开文件。
  • 使用拖放操作将文件拖放到网页上打开文件。
  • 使用 idb-file-storage 库将文件或 Blob 存储在本地,使用 IndexedDB。
  • 将文件传递到用户计算机上的本地应用程序。

对于每种机制,我们都会介绍其用法,并提供指向相关 API 文档、指南和任何展示如何使用 API 的示例的链接。

使用下载 API 下载文件

这种机制使您能够将文件从您的网站(或您可以定义为 URL 的任何位置)获取到用户的计算机上。关键方法是 downloads.download(),它在最简单的情况下接受一个 URL,并从该 URL 将文件下载到用户的默认下载文件夹

js
browser.downloads.download({ url: "https://example.org/image.png" });

您可以通过指定 saveAs 参数让用户下载到他们选择的位置。

注意: 使用 URL.createObjectURL() 您还可以下载在 JavaScript 中定义的文件和 Blob,其中可以包括从 IndexedDB 检索的本地内容。

下载 API 还提供了一些功能,例如取消、暂停、恢复、擦除和删除下载;在下载管理器中搜索已下载文件;在计算机的文件管理器中显示已下载文件;以及在关联的应用程序中打开文件。

要使用此 API,您需要在 manifest.json 文件中指定 "downloads" API 权限

示例:最新下载 API 参考:downloads API

使用文件选择器在扩展程序中打开文件

如果您想使用用户计算机上的文件,一种方法是让用户使用计算机的文件浏览器选择文件。要么创建一个新页面,要么在现有页面中注入代码以使用 HTML input 元素的 file 类型,向用户提供文件选择器。一旦用户选择了文件,与页面关联的脚本就可以使用 DOM File API 访问文件的内容,就像 Web 应用程序一样。

示例:Imagify 指南:使用 Web 应用程序中的文件 API 参考:HTML input 元素 | DOM File API

注意: 如果您想访问或处理选定文件夹中的所有文件,可以使用 <input type="file" webkitdirectory="true"/> 选择文件夹,并返回它包含的所有文件。

使用拖放操作在扩展程序中打开文件

Web 拖放 API 提供了一种使用文件选择器的替代方法。要使用此方法,请建立一个适合您 UI 的“放置区”,然后为 dragenterdragoverdrop 事件添加监听器。在放置事件的处理程序中,您的代码可以使用 DataTransfer.filesdataTransfer 属性提供的对象中访问用户拖放的任何文件。然后,您的代码可以使用 DOM File API 访问和操作这些文件。

示例:Imagify 指南:使用 Web 应用程序中的文件 | 文件拖放 API 参考:DOM File API

使用 IndexedDB 文件存储库将文件数据存储在本地

如果您的扩展程序需要在本地保存文件,idb-file-storage 库IndexedDB API 提供了一个简单的基于 Promise 的包装器,以帮助存储和检索文件和 Blob。

该库的主要功能是

getFileStorage

返回一个 IDBFileStorage 实例,如果该实例不存在,则创建名为的存储。

IDBFileStorage

提供用于保存和检索文件的方法,例如

  • list 用于获取数据库中可选过滤后的文件列表。
  • put 用于将文件或 Blob 添加到数据库。
  • get 用于从数据库中检索文件或 Blob。
  • remove 用于从数据库中删除文件或 Blob。

存储收集的图像 示例演示了如何使用大多数这些功能。

存储收集的图像示例允许用户使用图像上下文菜单上的选项将图像添加到集合中。选定的图像将收集在一个弹出窗口中,可以保存到命名的集合中。工具栏按钮 (browserAction) 打开一个导航集合页面,用户可以在该页面上查看和删除保存的图像,并提供一个筛选选项以缩小选择范围。 观看示例演示.

可以通过查看 /utils/ 中的 image-store.js 来了解该库的工作原理。

创建存储并保存图像

js
async function saveCollectedBlobs(collectionName, collectedBlobs) {
  const storedImages = await getFileStorage({ name: "stored-images" });

  for (const item of collectedBlobs) {
    await storedImages.put(`${collectionName}/${item.uuid}`, item.blob);
  }
}

当用户在弹出窗口中点击保存并为图像集合提供了名称时,将调用 saveCollectedBlobs

首先,getFileStorage 创建(如果尚未存在)或检索 IndexedDB 数据库 "stored-images" 到对象 storedImages 中。然后,storedImages.put() 使用 Blob 的唯一 ID(文件名)将每个收集的图像添加到数据库中,并将它们归类到集合名称下。

如果要存储的图像与数据库中已存在的图像具有相同的名称,则会覆盖该图像。如果您想避免这种情况,请先使用 imagesStore.list() 查询数据库,并使用文件名进行筛选;如果列表返回一个文件,请在要存储的新图像的名称中添加一个合适的后缀,以存储一个单独的项。

检索存储的图像以供显示

js
export async function loadStoredImages(filter) {
  const imagesStore = await getFileStorage({ name: "stored-images" });
  let listOptions = filter ? { includes: filter } : undefined;
  const imagesList = await imagesStore.list(listOptions);
  let storedImages = [];
  for (const storedName of imagesList) {
    const blob = await imagesStore.get(storedName);
    storedImages.push({ storedName, blobUrl: URL.createObjectURL(blob) });
  }
  return storedImages;
}

当用户在导航集合页面中点击查看或重新加载时,将调用 loadStoredImages()getFileStorage() 打开 "stored-images" 数据库,然后 imagesStore.list() 获取存储图像的过滤列表。然后,使用该列表使用 imagesStore.get() 检索图像,并构建一个列表以返回到 UI。

请注意使用 URL.createObjectURL(blob) 创建一个引用图像 Blob 的 URL。然后,该 URL 将在 UI 中 (navigate-collection.js) 用于显示图像。

删除收集的图像

js
async function removeStoredImages(storedImages) {
  const imagesStore = await getFileStorage({ name: "stored-images" });
  for (const storedImage of storedImages) {
    URL.revokeObjectURL(storedImage.blobUrl);
    await imagesStore.remove(storedImage.storedName);
  }
}

removeStoredImages() 在用户点击导航收藏页面中的删除按钮时被调用。同样,getFileStorage() 打开 "stored-images" 数据库,然后 imagesStore.remove() 从过滤后的图像列表中删除每个图像。

请注意使用 URL.revokeObjectURL() 显式撤销 blob URL。这使垃圾收集器能够释放分配给 URL 的内存。如果没有这样做,内存将不会在创建它的页面关闭之前返回。如果 URL 在扩展的后台页面中创建,则在扩展被禁用、卸载或重新加载之前,它不会被卸载,因此不必要地保留此内存可能会影响浏览器性能。如果 URL 在扩展的页面(新标签页、弹出窗口或侧边栏)中创建,则在页面关闭时释放内存,但撤销不再需要的 URL 仍然是一个好习惯。

一旦 blob URL 被撤销,任何尝试加载它的尝试都将导致错误。例如,如果 blob URL 用作 IMG 标签的 SRC 属性,则图像将不会加载,并且不可见。因此,在撤销 blob URL 时,从生成的 HTML 元素中删除任何被撤销的 blob URL 是一个好习惯。

示例:存储收集的图像 API 参考:idb-file-storage 库

注意: 您也可以使用完整的 Web IndexedDB API 来存储扩展程序中的数据。这在您需要存储 DOM Storage API 提供的简单键值对无法很好地处理的数据时很有用。

在本地应用程序中处理文件

如果您有一个本地应用程序或想为文件处理提供额外的本地功能,请使用本地消息传递将文件传递给本地应用程序进行处理。

您有两个选择

基于连接的消息传递

您使用 runtime.connectNative() 触发该过程,它会返回一个 runtime.Port 对象。然后,您可以使用 PortpostMessage() 函数将 JSON 消息传递给本地应用程序。使用 PortonMessage.addListener() 函数,您可以监听来自本地应用程序的消息。如果在调用 runtime.connectNative() 时本地应用程序没有运行,则会打开本地应用程序,并且应用程序将保持运行,直到扩展调用 Port.disconnect() 或连接到它的页面关闭。

无连接消息传递

您使用 runtime.sendNativeMessage() 将 JSON 消息发送到本地应用程序的新的临时实例。浏览器在从本地应用程序收到任何回复消息后关闭本地应用程序。

要添加您希望本地应用程序处理的文件或 blob,请使用 JSON.stringify().

要使用此方法,扩展程序必须在 manifest.json 文件中请求 "nativeMessaging" 权限可选权限。在使用可选权限时,请记住检查权限是否已授予,并在必要时使用 permissions API 向用户请求权限。反过来,本地应用程序必须通过在其应用程序清单的 "allowed_extensions" 字段中包含扩展程序的 ID 来授予扩展程序权限。

示例:本地消息传递(仅说明简单消息传递)指南:本地消息传递 API 参考:runtime API