downloads 下载
本教程共 56 篇 · 第 40 篇 · 更新于 2026-08-13 · 约 7 分钟阅读
本节目标:学完能触发浏览器下载、自定义文件名与保存路径,并管理下载项的查询、暂停、取消和记录擦除。
40-1 API 概览与权限
downloads API 让扩展以编程方式触发文件下载,还能监听和管理下载过程。常见用途:一键保存图片/视频、批量下载链接、把网页内容导出成本地文件、给下载文件自动归类命名。
它走的还是浏览器自带的下载器,所以文件会出现在下载栏里,用户也能像普通下载一样管理。
权限基础是 “downloads”。
{
"manifest_version": 3,
"permissions": ["downloads"]
}
下载动作由浏览器自带的下载器直接执行,不经过扩展的 fetch,所以跨域下载同样不需要 host_permissions——只要声明了 "downloads" 权限,就能下载任意 http(s) 地址(包括别的站点)。真正需要 host 权限的是 scripting 注入、fetch 请求这类操作。
Note下载「当前扩展内的资源」(比如 web_accessible_resources 暴露的文件)不需要额外权限;下载外部站点的文件同样只需
"downloads"权限,host_permissions 对 downloads API 没有要求。
40-2 触发一次下载:download
最核心的方法是 chrome.downloads.download,传一个配置对象,返回一个下载项 id。
chrome.downloads.download({
url: "https://example.com/report.pdf",
filename: "报告/report.pdf",
saveAs: false,
conflictAction: "uniquify"
}, (downloadId) => {
if (chrome.runtime.lastError) {
console.log("下载失败:", chrome.runtime.lastError.message);
} else {
console.log("已发起下载,id:", downloadId);
}
});
url 是必填,就是要下载的文件地址。filename 决定保存时的名字和相对路径(下一节细讲)。saveAs 为 true 时会弹出系统的「另存为」对话框让用户选位置;为 false 就直接按默认规则存。
conflictAction 处理「同名文件已存在」的情况,取值有 “uniquify”(自动改名避免冲突)、“overwrite”(覆盖)、“prompt”(让用户决定)。
Tipdownload 出错时不会抛异常,而是体现在 chrome.runtime.lastError 里。务必判断这个错误,不然下载静默失败你都不知道。
40-3 文件名与路径控制
文件名和路径是本章重点,也是这个 API 比单纯「另存为」好用的地方。
filename 是相对「默认下载目录」的路径,不是绝对路径。你可以写子文件夹,比如 “图片/头像.png”,浏览器会在下载目录里自动建好这个文件夹再存。
chrome.downloads.download({
url: "https://example.com/a.png",
filename: "收藏/头像.png",
conflictAction: "uniquify"
});
有两个硬性限制要记住。第一,filename 不能是绝对路径,也不能用 ”..” 往上层目录逃,只能在默认下载目录以内组织。第二,路径分隔用正斜杠 ”/”。
如果你想在下载「真正开始之前」动态决定文件名,用 chrome.downloads.onDeterminingFilename 事件。它在浏览器定文件名那一刻介入,让你 suggest 一个最终路径。
chrome.downloads.onDeterminingFilename.addListener((item, suggest) => {
const name = item.filename.replace(/\.tmp$/, ".png");
suggest({ filename: "自动归类/" + name, conflictAction: "uniquify" });
});
这个事件非常适合做「按类型自动归档」:图片进图片夹、文档进文档夹,完全不用用户操心。
NoteonDeterminingFilename 里必须用 suggest 回调给出结果,不能同步返回。不调用 suggest,下载会沿用默认文件名。
40-4 查询下载项:search 与 getFileIcon
想知道已经有哪些下载、它们的状态如何,用 chrome.downloads.search。
chrome.downloads.search({ state: "in_progress" }, (items) => {
items.forEach((it) => console.log(it.filename, it.bytesReceived + "/" + it.totalBytes));
});
search 的过滤条件可以是 id、state(“in_progress” 进行中 / “interrupted” 中断 / “complete” 完成)、query(关键字)等。不传条件就是全部。
DownloadItem 字段很丰富:id、url、filename(实际保存路径)、state、bytesReceived(已下字节)、totalBytes(总字节)、fileSize、startTime、endTime、exists(文件是否还在磁盘)、byExtensionId(发起扩展 id)等。
还有一个实用方法 chrome.downloads.getFileIcon(id),能拿到该下载项对应的文件类型图标,做下载管理器界面时很有用。
Tip判断下载进度,用 bytesReceived / totalBytes 算百分比;服务器没返回长度时 totalBytes 为 -1,计算前先判
totalBytes > 0,避免算出 NaN。
40-5 暂停、继续与取消
下载过程中可以中途控制。pause 暂停、resume 继续、cancel 取消。
// 暂停
chrome.downloads.pause(downloadId);
// 继续(仅当 item.canResume 为 true 时有效)
chrome.downloads.resume(downloadId);
// 取消
chrome.downloads.cancel(downloadId);
pause 之后,DownloadItem 的 state 会变成 “interrupted”,同时 paused 字段为 true。能不能 resume,要看 canResume 是否为 true——有些被中断的下载(比如网络错误)是无法续传的。
cancel 会中止下载,已下载的临时文件会被清理。做「下载管理器」类扩展时,这三个方法配合 search 出来的列表,就能做出完整的控制条。
Noteresume 只对可续传的下载有效。调用前先查一下对应 item 的 canResume,false 时就别调了,调了也没反应。
40-6 擦除记录:erase
注意,erase 擦除的是「下载历史记录」,不是磁盘上的文件。
chrome.downloads.erase({ id: downloadId }, (erasedIds) => {
console.log("已擦除记录:", erasedIds);
});
erase 接受的过滤条件和 search 类似。它只把下载栏里的这条记录删掉,文件本身是否保留取决于它当时是否已经完成并留在磁盘上。
如果你想「连文件一起删」,downloads API 本身做不到直接删文件。常见做法是先确认文件已完成,再通过别的手段处理,或者干脆只擦记录、保留文件,把选择权交给用户。
Tiperase 和「删文件」是两回事。要做隐私清理,擦记录只是第一步;真正删文件需要用户自己在下载栏操作,扩展无法越权删用户文件。
40-7 监听下载事件
下载过程有完整的事件链,方便你做进度条、自动归类、完成提示。
// 下载项刚被创建
chrome.downloads.onCreated.addListener((item) => {
console.log("开始下载:", item.filename);
});
// 状态或进度变化(进度更新会频繁触发)
chrome.downloads.onChanged.addListener((delta) => {
if (delta.state && delta.state.current === "complete") {
console.log("下载完成,id:", delta.id);
}
});
// 记录被擦除
chrome.downloads.onErased.addListener((downloadId) => {
console.log("记录已擦除:", downloadId);
});
onChanged 的 delta 对象只带「发生变化」的字段,比如 delta.bytesReceived、delta.state,每个字段又有 previous 和 current。进度类变化触发很密,做 UI 时注意节流,别每次都重绘整个列表。
还记得上一节的 onDeterminingFilename 吗?它也属于下载事件,用于定文件名,二者常配合:onDeterminingFilename 定路径,onChanged 跟踪进度。
40-8 常见坑与调试
第一个坑,忘了判断 chrome.runtime.lastError。download 失败时不会抛错,只在 lastError 里留信息,不查就以为成功了。
第二个坑,filename 写了绝对路径或 ”..”。浏览器只允许在默认下载目录内组织,越界的写法会被忽略或报错。
第三个坑,误以为跨域下载需要 host 权限。downloads API 不需要 host_permissions,只要 "downloads" 权限就能下载任意 http(s) 地址;下载被拦时先看 chrome.runtime.lastError 的具体信息,别往 host 权限上找。
第四个坑,把 erase 当成删文件。它只清记录,不碰磁盘文件,别误导用户。
调试时打开后台服务工作者(Service Worker)控制台,download 的报错都会在那里。也可以在 popup 里放几个按钮直接触发 download,配合下载栏实时观察文件名和路径是否符合预期。
Tip文件名不生效时,先确认有没有被 onDeterminingFilename 里的 suggest 覆盖;下载报错时先看 chrome.runtime.lastError 的具体信息,别怀疑 host 权限。
40-9 小结
downloads API 的职责就是「触发并管好下载」。download 发起、search 查询、pause/resume/cancel 控制、erase 擦记录,再加上 onDeterminingFilename 定文件名、onChanged 跟进度,一套下来功能很完整。
文件名控制是精华:filename 支持相对子目录,onDeterminingFilename 能在下载前动态归类,conflictAction 决定同名冲突怎么处理。
两个易混点再强调一遍:filename 只能在默认下载目录内、不能用绝对路径;erase 只删记录不删文件。
最后提醒:下载涉及用户磁盘和隐私,批量下载前最好让用户确认,别默默把一堆文件塞进人家下载目录。跨域下载只需 "downloads" 权限,无需额外配 host 权限。