使用文件
您的浏览器扩展程序可能需要使用文件才能实现全部功能。本文将介绍处理文件的五种机制。
- 将文件下载到用户选定的下载文件夹。
- 使用网页上的文件选择器打开文件。
- 使用拖放方式将文件拖放到网页上打开。
- 使用 idb-file-storage 库将文件或 blob 本地存储到 IndexedDB 中。
- 将文件传递给用户计算机上的原生应用程序。
对于每种机制,我们都将介绍其用法,并引用相关的 API 文档、指南和使用 API 的示例。
使用 Downloads API 下载文件
此机制使您能够将文件从您的网站(或任何您定义为 URL 的位置)传输到用户计算机。关键方法是 downloads.download(),它最简单的形式接受一个 URL,并将该 URL 的文件下载到用户的默认下载文件夹。
browser.downloads.download({ url: "https://example.org/image.png" });
您可以通过指定 saveAs 参数,让用户下载到他们选择的位置。
注意:使用 URL.createObjectURL(),您还可以下载 JavaScript 中定义的文件和 blob,包括从 IndexedDB 检索到的本地内容。
Downloads API 还提供了取消、暂停、恢复、擦除和删除下载的功能;在下载管理器中搜索已下载文件;在计算机的文件管理器中显示已下载文件;以及在关联的应用程序中打开文件。
要使用此 API,您需要在 manifest.json 文件中指定 "downloads" API 权限。
示例:Latest download API 参考:downloads API
使用文件选择器在扩展程序中打开文件
如果您想处理用户计算机上的文件,一种选择是让用户使用计算机的文件浏览器选择文件。您可以创建一个新页面,或者将代码注入现有页面,以使用 HTML input 元素的 file 类型为用户提供文件选择器。用户选择文件后,与页面关联的脚本就可以像 Web 应用程序一样使用 DOM File API 访问文件内容。
示例:Imagify 指南:使用 Web 应用程序中的文件 API 参考:HTML input 元素 | DOM File API
注意:如果您想访问或处理所选文件夹中的所有文件,可以使用 <input type="file" webkitdirectory="true"/> 来选择文件夹并返回其中包含的所有文件。
使用拖放方式在扩展程序中打开文件
Web Drag and Drop API 提供了一种替代文件选择器的方法。要使用此方法,请建立一个符合您 UI 的“放置区”,然后在该元素上添加 dragenter、dragover 和 drop 事件的侦听器。在 drop 事件的处理程序中,您的代码可以使用 DataTransfer.files 属性提供的对象访问用户拖放的任何文件。然后,您的代码可以使用 DOM File API 访问和操作这些文件。
示例:Imagify 指南:使用 Web 应用程序中的文件 | 文件拖放 API 参考:DOM File API
使用 IndexedDB 文件存储库本地存储文件数据
如果您的扩展程序需要在本地保存文件,idb-file-storage 库 提供了一个简单的基于 Promise 的 IndexedDB API 封装,用于方便地存储和检索文件和 blobs。
该库的关键功能包括:
- getFileStorage
-
返回一个
IDBFileStorage实例,如果命名的存储不存在,则创建它。 - IDBFileStorage
-
提供保存和检索文件的各种方法,例如:
- list 用于获取数据库中文件的可选过滤列表。
- put 用于将文件或 blob 添加到数据库。
- get 用于从数据库检索文件或 blob。
- remove 用于从数据库删除文件或 blob。
Store Collected Images 示例演示了如何使用这些功能的大部分。
Store Collected Images 示例允许用户通过图像上下文菜单中的一个选项将图像添加到集合中。选定的图像会收集在弹出窗口中,并可以保存到命名的集合中。一个工具栏按钮(browserAction)会打开一个导航集合页面,用户可以在其中查看和删除已保存的图像,并提供筛选选项来缩小选择范围。 观看示例的实际演示。
可以通过查看 /utils/ 中的 image-store.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() 查询数据库;如果列表返回了一个文件,则为新图像的名称添加一个合适的后缀,以存储一个单独的项。
检索已存储的图像以供显示
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)中使用以显示图像。
删除收集的图像
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 直到扩展程序被禁用、卸载或重新加载后才会被卸载,因此不必要地占用此内存可能会影响浏览器性能。如果在扩展程序的页面(新标签页、弹出窗口或侧边栏)中创建了 URL,则在关闭页面时内存会被释放,但仍建议在不再需要 URL 时撤销它。
一旦 blob URL 被撤销,任何尝试加载它的行为都会导致错误。例如,如果 blob URL 被用作 IMG 标签的 SRC 属性,图像将不会加载并且不可见。因此,在撤销 blob URL 时,最好将其从生成的 HTML 元素中移除。
示例:Store Collected Images API 参考:idb-file-storage 库
注意:您也可以使用完整的 Web IndexedDB API 来存储扩展程序的数据。这在您需要存储 DOM Storage API 提供的简单键/值对无法很好处理的数据时可能很有用。
在本地应用程序中处理文件
当您拥有一个原生应用程序或想要为文件处理提供额外的原生功能时,可以使用原生消息传递将文件传递给原生应用程序进行处理。
您有两种选择:
- 基于连接的消息传递
-
在这里,您通过
runtime.connectNative()触发该过程,它返回一个runtime.Port对象。然后,您可以使用Port的postMessage()函数将 JSON 消息传递给原生应用程序。使用Port的onMessage.addListener()函数,您可以侦听来自原生应用程序的消息。当调用runtime.connectNative()时,如果原生应用程序未运行,则会打开它,并且该应用程序将保持运行状态,直到扩展程序调用Port.disconnect()或连接到它的页面关闭。 - 无连接消息传递
-
在这里,您使用
runtime.sendNativeMessage()将 JSON 消息发送到原生应用程序的一个新、临时的实例。浏览器会在收到原生应用程序的任何消息后关闭原生应用程序。
要添加原生应用程序要处理的文件或 blob,请使用 JSON.stringify()。
要使用此方法,扩展程序必须在其 manifest.json 文件中请求 "nativeMessaging" 权限或可选权限。如果使用可选权限,请记住检查该权限是否已被授予,并在必要时使用 permissions API 向用户请求权限。反之,原生应用程序必须在其应用程序清单的 "allowed_extensions" 字段中包含其 ID,以授予扩展程序权限。
示例:Native Messaging(仅演示简单消息传递)指南:Native messaging API 参考:runtime API