拓冰建站拓冰建站
首页 / 资讯中心 / 正文

chrome-extensions-samples 实战:基于 chrome.downloads API 构建完整的 Download Manager 扩展

示例工程【免费下载链接】chrome-extensions-samplesChrome Extensions Samples项目地址https://gitcode.com/gh_mirrors/ch/chrome-extensions-samples点击查看免费下载本文以chrome-extensions-samples仓库中的 api-samples/downloads/download_manager 示例为核心系统讲解如何基于 Manifest V3MV3的chrome.downloadsAPI 家族实现一个功能完整的下载管理器扩展包括下载项的查询与实时渲染、暂停/恢复/取消/删除等操作、危险文件处理、搜索过滤以及 Service Worker 中基于 OffscreenCanvas 的动态工具栏图标与进度轮询。读完本文你将掌握chrome.downloads核心 API 的调用模式以及如何在一个 MV3 扩展中组织 popup 页面与后台 Service Worker 的分工协作。示例概览一个 Download Manager Button 扩展该示例位于 api-samples/downloads/download_manager是一个完整的 MV3 扩展官方 README 用一句话概括了它的定位该示例使用了多个chrome.downloadsAPI 来实现一个简单的下载管理器Download Manager。实际代码远比简单丰富。它通过扩展的 action 按钮popup展示当前浏览器的下载列表并支持对每一条下载记录执行一系列操作同时把工具栏图标变成一枚实时状态徽章——正在下载时显示进度弧、存在危险文件时显示红色危险角标、暂停时显示暂停符号、有新完成项时显示绿色对勾。扩展的文件结构如下api-samples/downloads/download_manager/ ├── _locales/en/messages.json # i18n 本地化文案 ├── manifest.json # MV3 清单 ├── popup.html / popup.css # 弹出层界面与样式 ├── popup.js # 弹出层核心逻辑约 767 行 ├── service-worker.js # 后台 Service Worker约 233 行 ├── icons.html / icons.js # 辅助工具页重新生成 Manifest 图标 ├── icon128.png / icon19.png / icon38.png └── README.md其中 popup 与后台的分工非常清晰popup.js 负责下载列表的展示与用户交互service-worker.js 负责需要持续运行的状态监控与动态图标绘制。这种UI 密集逻辑放 popup、后台只做轻量监控的架构正是 MV3 扩展的推荐实践因为 popup 只在用户点击工具栏按钮时短暂存在而 Service Worker 会因事件被唤醒。运行与调试三步加载一个未打包扩展官方 README 给出的运行步骤非常简洁共三步克隆本仓库chrome-extensions-samples。在 Chrome 中以加载已解压的扩展程序Load unpacked方式加载api-samples/downloads/download_manager目录。点击扩展的工具栏按钮打开 popup。补充几个实际运行中的细节加载入口打开chrome://extensions开启开发者模式点击加载已解压的扩展程序选择api-samples/downloads/download_manager目录即可。加载后浏览器会读取 manifest.json 完成注册。触发下载由于chrome.downloads的事件监听器onCreated/onChanged/onErased会唤醒 Service Worker即使 popup 未打开工具栏图标也会被动态更新点击图标后 popup 打开会立即执行一次全量查询并渲染列表。辅助图标生成页目录内的 icons.html 是一个独立的小工具页Generate Manifest Icons点击按钮会通过chrome.runtime.sendMessage(icons)通知 Service Worker用 OffscreenCanvas 重新绘制icon16/19/38/128.png等图标并触发下载见 service-worker.js便于扩展作者重新生成自己的图标资源。manifest.json 解析downloads 权限族与可选权限扩展的核心配置在 manifest.json完整内容如下{ name: Download Manager Button, version: 0.3, manifest_version: 3, description: Uses multiple chrome.downloads APIs to implement a simple download manager., icons: { 128: icon128.png }, action: { default_icon: { 19: icon19.png, 38: icon38.png }, default_title: __MSG_extName__, default_popup: popup.html }, background: { service_worker: service-worker.js }, default_locale: en, optional_permissions: [management], permissions: [downloads, downloads.open, downloads.ui, storage] }几个关键点值得逐一说明配置项值作用manifest_version3使用 Manifest V3后台逻辑运行在 Service Worker 中对应background.service_worker字段permissionsdownloadschrome.downloadsAPI 的查询/管理权限本示例几乎所有功能都依赖它permissionsdownloads.open允许调用chrome.downloads.open()打开已下载文件permissionsdownloads.ui允许调用chrome.downloads.setUiOptions()关闭浏览器原生的下载 UI下载托盘由本扩展自绘状态permissionsstorage使用chrome.storage.local与chrome.storage.session记录 popup 打开时间、图标缓存、权限拒绝状态等optional_permissionsmanagement可选权限仅在用户点击Show links to extensions that download files并授权后才通过chrome.permissions.request()申请用于显示由哪个扩展发起的下载链接action.default_popuppopup.html点击工具栏图标时打开的弹出层default_localeen声明默认语言为英文配合_locales/en/messages.json使用关于downloads.ui权限有一个非常重要的细节获得该权限的扩展通常是下载管理器类扩展调用chrome.downloads.setUiOptions({ enabled: false })后Chrome 会隐藏自带的下载托盘/下载条将下载状态的呈现完全交给扩展。这正是 service-worker.js 的第一行代码所做的事情chrome.downloads.setUiOptions({ enabled: false });从源码结构看本扩展正是借助这一能力接管了下载进度的可视化用工具栏动态图标替代系统下载托盘。这也是为什么它需要同时申请downloads、downloads.open、downloads.ui三个权限的原因。弹出层架构DownloadItem 类与下载列表渲染popup 的全部逻辑位于 popup.js其核心是一个DownloadItem类popup.js它封装了一条下载记录的全部数据与操作数据绑定构造函数遍历chrome.downloads.search()返回的 DownloadItem 数据对象把state、bytesReceived、totalBytes、filename、referrer、danger、paused、startTime、estimatedEndTime、endTime、byExtensionId等字段全部复制到实例上并把startTime解析为Date对象popup.js。DOM 克隆popup.html 中定义了一个隐藏的.item模板节点DownloadItem通过cloneNode(true)克隆模板生成列表项并以item{id}作为 DOM idpopup.js。按开始时间排序插入新项插入列表时先用binarySearch按startTime时间戳二分定位插入位置再插入到对应 DOM 节点前后保证列表始终按开始时间从新到旧排列[popup.js](https://link.gitcode.com/i/f6afd7332af812c00784f4b3ab11588f#L151-L168, L185-L216)。事件绑定每个操作按钮都绑定到对应 API 调用——open调chrome.downloads.open(id)、show调chrome.downloads.show(id)、removeFile调chrome.downloads.removeFile(id)、erase调chrome.downloads.erase({id})、pause/resume/cancel分别调chrome.downloads.pause/resume/cancel(id)popup.js。render()方法popup.js负责把数据状态映射为界面表现是理解整个列表渲染的关键状态判定in_progress state in_progressopenable state ! interrupted exists !deleted。文件仍可打开时显示打开文件名链接否则显示灰化的Removed文本popup.js。文件图标若已有filename且尚无icon_url调用chrome.downloads.getFileIcon(id, { size: 32 })异步获取文件类型图标popup.js。按钮显隐矩阵暂停/恢复/取消按钮仅在in_progress时出现恢复按钮额外要求paused为真remove-file仅当state complete且文件存在、未被删除、且chrome.downloads.removeFile可用时显示erase在下载进行中隐藏popup.js。进度条与剩余时间当in_progress || canResume时显示进度区有totalBytes时显示bytesReceived/totalBytes及按百分比填充的 meter 进度条estimatedEndTime存在且未暂停时用formatTimeLeft()把剩余毫秒格式化为 Xd Yh left / Xh Ym left / Xm Ys left / Xs left[popup.js](https://link.gitcode.com/i/f6afd7332af812c00784f4b3ab11588f#L365-L447, L120-L139)。危险文件提示maybeAccept()是渲染的收尾动作popup.js——只要某条记录处于in_progress且danger既不是safe也不是accepted就调用chrome.downloads.acceptDanger(id)触发 Chrome 原生的危险下载确认对话框并用一个类级标志accepting_danger防止并发重复弹窗。数据驱动search 查询、事件监听与进度轮询popup 里的数据不是静态快照而是由事件驱动 定时轮询两条链路共同维持的初次加载chrome.downloads.search分页取数DownloadManager.loadItems()在脚本加载时立即执行不等待window.onload见 popup.js用orderBy和limit做分页查询const kShowNewMax 50; const kOldMs 1000 * 60 * 60 * 24 * 7; // 7 天 const results await chrome.downloads.search({ orderBy: [-startTime], limit: kShowNewMax 1 });这里有个精巧的探针设计popup.js查询kShowNewMax 151条但只展示 50 条——多出来的第 51 条用于探测是否存在更早的下载。如果确实存在更早记录就显示Show Older Downloads按钮。默认展示策略是优先展示 7 天kOldMs内、最多 50 条新记录如果一条新的都没有则退而展示任意时间的最多 50 条见showNew()的兜底逻辑popup.js。点击Show Older后showOlder()会执行一次不带任何过滤条件的全量search({})把隐藏的旧记录全部显示出来同时显示Loading Older Downloads...占位popup.js。事件监听onCreated / onChanged / onErasedpopup 注册了三个事件监听器popup.jschrome.downloads.onCreated新下载创建时getOrCreate()拿到或新建DownloadItem刷新列表并启动进度轮询。chrome.downloads.onChanged任意字段变化时把 delta 中的current值合并进实例并重新render()当状态变为in_progress且未暂停时启动进度轮询popup.js。chrome.downloads.onErased下载从历史中删除时从 DOM 移除对应列表项并重新加载。值得强调的是代码注释里明确提到的一个坑bytesReceived的变化永远不会触发onChanged事件见 service-worker.js 的注释所以实时进度只能靠轮询。popup 的进度轮询间隔为200msDownloadManager.startPollingProgress.MS 200; DownloadManager.startPollingProgress.pollProgress async function () { const results await chrome.downloads.search({ state: in_progress, paused: false }); // 更新所有进行中记录并继续下一轮 };轮询用setTimeout自续期而非setInterval且只在存在进行中且未暂停的下载时才启动避免空闲时反复唤醒popup.js。搜索支持引号的 token 化查询顶部搜索框通过onsearch事件触发DownloadManager.onSearch()popup.js。查询串会按空格分词、引号内整体保留的规则拆成多个 termlet query document.getElementById(q).value.match(/(?:[^\s]|[^]*)/g); // 逐个 strip 掉成对的引号然后调用 const results await chrome.downloads.search({ query: query });即搜索foo bar baz会被解析为foo bar整体与baz两个关键词交给chrome.downloads.search的query参数做匹配无结果时显示Zero matches占位。搜索期间会隐藏Show Older入口并显示Teleporting lots of goats...本地化后的searching文案。打开文件夹showDefaultFolderpopup 顶部的文件夹图标调用chrome.downloads.showDefaultFolder()直接打开系统的默认下载目录popup.js。可选权限实战按需申请 management 权限这是示例中非常值得学习的一个模式下载记录里可能带有byExtensionId/byExtensionName由哪个扩展发起的下载但展示来源扩展的链接需要management权限。示例没有在安装时强要该权限而是在 manifest 里声明为optional_permissions: [management]渲染时用chrome.permissions.contains({ permissions: [management] })检查是否已授权popup.js未授权时显示一条提示条与Show links to extensions that download files链接点击后通过chrome.permissions.request({ permissions: [management] })弹出系统授权对话框若用户拒绝则把managementPermissionDenied写入chrome.storage.local下次不再重复打扰用户popup.js。授权成功后每条由扩展发起的下载会显示来源扩展图标通过chrome://extension-icon/{id}/48/1加载和名称链接指向chrome://extensions#{extensionId}popup.js。注意show-folder、referrer跳转到来源页面、open等链接的目标 URL 均在render()中动态赋值。Service WorkersetUiOptions、动态图标与 1 秒轮询后台 service-worker.js 承担三类任务1. 关闭原生下载 UI第一行即chrome.downloads.setUiOptions({ enabled: false })需要downloads.ui权限前文已述。2. OffscreenCanvas 动态绘制工具栏图标drawIcon(side, options)使用OffscreenCanvas在 Service Worker 中直接绘制图标service-worker.js绘制逻辑包括进度弧存在进行中下载时按totalBytesReceived / totalTotalBytes的比例绘制绿色圆弧若某些下载缺少totalBytes服务器未给出总大小则绘制 16 段的未知进度旋转条纹下载箭头中央固定的下载箭头状态角标优先级从高到低存在danger非 safe/accepted 的记录 → 红色危险角标否则存在暂停项 → 灰色暂停符号否则存在自上次打开 popup 以来新完成的项 → 绿色完成对勾。绘制完成后通过chrome.action.setIcon({ imageData })一次性设置 19px 与 38px 两档图标service-worker.js。3. 1 秒轮询驱动图标刷新与 popup 的 200ms 轮询不同后台的pollProgress()以1000ms间隔运行pollProgress.MS 1000service-worker.js同样只在存在进行中下载时自续期。它执行一次全量chrome.downloads.search({})聚合出上述图标状态并用chrome.storage.session缓存上一次的图标 JSON 快照——只有状态真正变化时才调用setIcon避免无意义的重复绘制service-worker.js。后台的唤醒路径覆盖了所有相关事件onCreated、onChanged状态变化会唤醒已卸载的 Worker、以及 popup 通过chrome.runtime.sendMessage(poll)发来的主动请求service-worker.js。popup 每次打开时setLastOpened()会记录popupLastOpened时间戳并发送poll消息从而让新完成下载角标的判定以上一次打开 popup 为时间基准。界面与国际化popup.html / popup.css / messages.jsonpopup.html 定义了完整的界面骨架顶部固定搜索栏#q、清除全部按钮#clear-all遍历可见项逐个erase()、打开下载文件夹按钮、下载项列表容器#items、隐藏的.item模板、以及危险文件授权提示条。所有操作按钮均使用内联 SVG 绘制图标暂停/恢复/取消/删除/擦除/显示目录/引用页/来源扩展无需额外图片资源。popup.css 实现了紧凑的单行布局white-space: nowrap、悬浮操作菜单.more绝对定位、以及带条纹动画的进度条.meter span:afterkeyframes move2 秒线性循环。国际化方面messages.json 定义了全部可见文案按钮 title、搜索占位符、空列表/零结果提示、12 个月份缩写、以及带占位符的剩余时间模板如$days$d $hours$h left。popup.js 的loadI18nMessages()在窗口加载时统一把chrome.i18n.getMessage()的结果写入 DOM并会根据本地化文案长度动态调整 popup 的最小宽度ratchetWidth/ratchetHeight保证不同语言下 UI 不换行、不截断。formatDateTime()还根据日期距离当前时间的远近智能显示HH:MMam/pm、日 月缩写或年份popup.js。本示例覆盖的 chrome.downloads API 全景将全部源码整理后可得到本示例实际使用到的 API 清单这也是一个下载管理器类扩展的最低完整能力集API用途出现位置chrome.downloads.search()按orderBy/limit/state/paused/query查询下载记录[popup.js](https://link.gitcode.com/i/f6afd7332af812c00784f4b3ab11588f#L573-L576, L691-L694, L636, L662)chrome.downloads.pause()/resume()/cancel()暂停 / 恢复 / 取消下载popup.jschrome.downloads.erase()从下载历史中移除记录popup.jschrome.downloads.removeFile()删除已下载的文件popup.jschrome.downloads.open()打开已完成的文件popup.jschrome.downloads.show()在系统文件管理器中定位文件popup.jschrome.downloads.showDefaultFolder()打开系统默认下载目录popup.jschrome.downloads.getFileIcon()获取文件类型图标popup.jschrome.downloads.acceptDanger()触发危险下载确认popup.jschrome.downloads.setUiOptions()接管/关闭浏览器原生下载 UIservice-worker.jschrome.downloads.download()发起新下载生成图标场景service-worker.jschrome.downloads.onCreated/onChanged/onErased下载生命周期事件popup.jschrome.action.setIcon()动态更新工具栏图标service-worker.jschrome.permissions.request()/contains()按需申请可选权限popup.jschrome.storage.local / session持久化与图标快照缓存[popup.js](https://link.gitcode.com/i/f6afd7332af812c00784f4b3ab11588f#L11, L394-L407)、service-worker.jschrome.runtime.sendMessage / onMessagepopup 与 Worker 通信popup.js、service-worker.js两个值得借鉴的实现细节最后提炼示例中两个可复用的工程技巧少查询一条的探针分页loadItems查询 51 条只展示 50 条用多出的 1 条判断是否有更早记录从而决定是否显示Show Older按钮——这是避免额外一次 API 调用的低成本做法popup.js。两个层级的差异化轮询popup 需要高刷新率的进度条用 200ms 轮询Service Worker 只关心工具栏徽章的整体状态用 1000ms 轮询且仅在存在进行中下载时自续期、仅在状态快照变化时重绘。这种按需唤醒、按变化绘制的策略能显著减少后台 Worker 的空转与绘制开销。参考同仓库其他 downloads 示例downloads_overwrite、download_links、download_filename_controller可以进一步看到chrome.downloads在文件命名、批量下载、下载触发等场景的更多用法而本示例聚焦的是下载完成后的全生命周期管理二者互补。若要在自己的项目中复刻直接以本示例的 manifest.json 为起点按上文权限表裁剪权限即可快速落地一个拥有完整下载管理能力的 MV3 扩展。赞分享示例工程【免费下载链接】chrome-extensions-samplesChrome Extensions Samples项目地址https://gitcode.com/gh_mirrors/ch/chrome-extensions-samples点击查看免费下载相关推荐如何快速上手 WrenAI从安装到第一次跑通自然语言查询如何快速上手 WrenAI从安装到第一次跑通自然语言查询 WrenAI 是一个开源的生成式 BIGenBI引擎它让 AI 智能体通过受治理的 text示例工程Chrome 扩展 web_accessible_resources 完整指南基于 chrome-extensions-samples 的实战示例解析Chrome 扩展 web_accessible_resources 完整指南基于 chrome extensions samples 的实战示例解析 本指南示例工程Hindsight微服务架构将记忆系统拆分为独立服务的完整指南Hindsight微服务架构将记忆系统拆分为独立服务的完整指南 Hindsight作为一款先进的AI代理记忆系统其微服务架构设计为大规模生产部署提供了强大的示例工程上一篇DeepSeek-LLM部署实战7B/67B模型GPU配置完全指南下一篇从Electron迁移到PakePlus的终极指南20倍体积缩减与性能提升创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门