background.js中可通过注入脚本的方法实现获取blobUrl实现自定义下载
2025-09-22 00:19:17 64 分享链接 开发笔记 浏览器扩展
manifest.json 添加 scripting 权限才能注入临时脚本
{
"manifest_version": 3,
"name": "Blob 下载重命名工具",
"version": "1.0",
"permissions": [
"downloads",
"scripting",
"activeTab"
],
"background": {
"service_worker": "background.js"
}
}
background.js 监听所有下载并重写方法
chrome.downloads.onCreated.addListener(async (downloadItem) => {
console.log('检测到新下载:', downloadItem.url);
// 只处理 blob 类型的下载
if (!downloadItem.url.startsWith('blob:')) {
return;
}
// 1. 先取消当前下载
chrome.downloads.cancel(downloadItem.id, async () => {
const cancelError = chrome.runtime.lastError;
if (cancelError) {
console.error('取消下载失败:', cancelError);
return;
}
try {
// 2. 生成新文件名(自定义命名规则)
const timestamp = new Date().getTime();
let newFileName = `custom_${timestamp}`;
// 尝试从原文件名提取扩展名
if (downloadItem.filename) {
const ext = downloadItem.filename.split('.').pop();
newFileName = `custom_${timestamp}.${ext}`;
}
// 3. 获取当前活跃标签页,用于处理 blob
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab) {
console.error('未找到活跃标签页');
return;
}
// 4. 通过 content script 获取 blob 数据
const results = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: (blobUrl) => {
return new Promise(async (resolve) => {
try {
// 在页面上下文获取 blob 数据
const response = await fetch(blobUrl);
const blob = await response.blob();
// 转换为可下载的 data URL
const fileReader = new FileReader();
fileReader.onload = () => {
resolve({
dataUrl: fileReader.result,
mimeType: blob.type
});
};
fileReader.readAsDataURL(blob);
} catch (error) {
console.error('获取 blob 数据失败:', error);
resolve(null);
}
});
},
args: [downloadItem.url] // 传递 blob URL 给 content script
});
const blobData = results[0].result;
if (!blobData) {
console.error('无法获取 blob 数据');
return;
}
// 5. 用新文件名重新下载
chrome.downloads.download({
url: blobData.dataUrl, // 使用转换后的 data URL
filename: newFileName,
conflictAction: 'uniquify'
}, (downloadId) => {
const downloadError = chrome.runtime.lastError;
if (downloadError) {
console.error('重新下载失败:', downloadError);
} else {
console.log(`已用新文件名下载:${newFileName},ID:${downloadId}`);
}
});
} catch (error) {
console.error('处理 blob 下载时出错:', error);
}
});
});
处理 blob 类型的下载并修改文件名需要特殊处理,因为 blob URL 是临时的且仅在创建它的页面上下文有效。以下是实现方案:
实现说明
核心原理:
- blob URL 只能在创建它的页面上下文中访问,所以需要通过 content script 在页面环境中处理
- 将 blob 数据转换为 data URL(可以跨上下文使用),再用新文件名重新下载
关键步骤:
- 检测到 blob 类型下载时先取消原下载
- 通过
chrome.scripting.executeScript
向当前页面注入临时脚本 - 在页面上下文中使用
fetch
获取 blob 数据并转换为 data URL - 使用转换后的 data URL 和自定义文件名重新发起下载
自定义文件名:
- 目前使用
custom_时间戳.扩展名
的格式 你可以修改
newFileName
的生成逻辑,例如:// 示例:根据原文件名和日期生成 const date = new Date().toISOString().split('T')[0]; newFileName = `${date}_${baseName}.${ext}`;
- 目前使用
权限要求:
- 必须添加
scripting
权限才能注入临时脚本 activeTab
权限用于获取当前活跃标签页
- 必须添加
这种方法可以有效处理 blob 类型的下载并重命名,解决了 blob URL 跨上下文访问的限制。
详细给我讲一下注入临时脚本
在 Chrome 扩展开发中,“注入临时脚本”指的是通过扩展的后台脚本(如 service worker)向当前活跃的网页中动态插入一段 JavaScript 代码,这段代码会在网页的上下文(而非扩展的上下文)中执行。这种技术常用于解决跨上下文访问限制(比如你遇到的 blob URL 访问问题)。
为什么处理 blob 下载需要注入临时脚本?
- 上下文隔离:Chrome 扩展的后台脚本(
background.js
)运行在独立的扩展上下文,与网页的上下文完全隔离。 - blob URL 限制:
blob:
协议的 URL 是临时的,且仅在创建它的网页上下文内有效,后台脚本无法直接访问。 - 权限边界:网页内的资源(如 blob 数据)受浏览器安全策略限制,只能在网页自身的上下文内读取。
因此,必须通过“临时脚本注入”的方式,让代码在网页上下文内执行,才能获取 blob 数据并重新处理。
注入临时脚本的完整流程(结合你的需求)
以下是针对“blob 下载重命名”场景的注入脚本实现,包含详细步骤说明:
1. 后台脚本触发注入(background.js
)
当检测到 blob 类型的下载时,取消原下载,然后向当前网页注入脚本:
chrome.downloads.onCreated.addListener((downloadItem) => {
console.log('检测到下载:', downloadItem.url);
// 只处理 blob 类型的下载
if (!downloadItem.url.startsWith('blob:')) return;
// 步骤1:取消原下载
chrome.downloads.cancel(downloadItem.id, () => {
const cancelError = chrome.runtime.lastError;
if (cancelError) {
console.error('取消下载失败:', cancelError);
return;
}
// 步骤2:获取当前活跃标签页(blob 所在的页面)
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const targetTab = tabs[0];
if (!targetTab) {
console.error('未找到活跃标签页');
return;
}
// 步骤3:注入临时脚本到目标标签页
chrome.scripting.executeScript({
target: { tabId: targetTab.id }, // 注入到哪个标签页
func: fetchBlobAndConvert, // 要注入的函数(核心逻辑)
args: [downloadItem.url] // 传递给注入函数的参数(blob URL)
}).then((results) => {
// 步骤5:处理注入脚本的返回结果
if (chrome.runtime.lastError) {
console.error('注入脚本失败:', chrome.runtime.lastError);
return;
}
// 注入函数的返回值在 results[0].result 中
const { dataUrl, originalName } = results[0].result;
if (!dataUrl) {
console.error('获取 blob 数据失败');
return;
}
// 步骤6:用新文件名重新下载
const newFileName = `custom_${originalName || 'download_' + Date.now()}.pdf`; // 自定义文件名
chrome.downloads.download({
url: dataUrl,
filename: newFileName,
conflictAction: 'uniquify'
}, (newId) => {
if (chrome.runtime.lastError) {
console.error('重新下载失败:', chrome.runtime.lastError);
} else {
console.log('已用新文件名下载,ID:', newId);
}
});
});
});
});
});
2. 注入到网页的临时脚本(核心逻辑)
fetchBlobAndConvert
是要注入到网页的函数,在网页上下文执行,负责读取 blob 数据并转换为可跨上下文使用的 data URL:
// 这个函数会被注入到网页中执行
function fetchBlobAndConvert(blobUrl) {
return new Promise((resolve) => {
// 步骤4:在网页上下文内请求 blob 数据
fetch(blobUrl)
.then(response => {
// 尝试从响应头获取原始文件名
const contentDisposition = response.headers.get('Content-Disposition');
let originalName = null;
if (contentDisposition) {
// 解析文件名(处理浏览器兼容)
const match = contentDisposition.match(/filename="?([^";]+)"?/);
if (match) originalName = match[1];
}
// 将响应转换为 blob
return response.blob().then(blob => ({ blob, originalName }));
})
.then(({ blob, originalName }) => {
// 将 blob 转换为 data URL(可在扩展上下文使用)
const reader = new FileReader();
reader.onloadend = () => {
// 转换后的 data URL 格式:data:application/pdf;base64,...
resolve({ dataUrl: reader.result, originalName });
};
reader.readAsDataURL(blob);
})
.catch(error => {
console.error('网页内处理 blob 失败:', error);
resolve({ dataUrl: null });
});
});
}
关键技术点解析
chrome.scripting.executeScript
参数:target.tabId
:指定注入的目标标签页 ID(必须是当前活跃页,否则可能无权限)。func
:要注入的函数(注意:函数体内不能直接引用扩展上下文的变量,只能通过args
传递参数)。args
:传递给注入函数的参数(必须是可序列化的数据,如字符串、数字、简单对象)。
注入脚本的权限范围:
- 注入的脚本运行在网页上下文,可访问网页的
window
、document
等对象。 - 不能直接调用 Chrome 扩展 API(如
chrome.downloads
),必须通过返回值将数据传递给后台脚本。
- 注入的脚本运行在网页上下文,可访问网页的
数据转换逻辑:
fetch(blobUrl)
:在网页上下文内请求 blob 数据(此时 URL 有效)。readAsDataURL(blob)
:将 blob 转换为 data URL(一种可跨上下文传递的 base64 编码字符串)。
安全性注意:
- 注入的脚本受网页的 CSP(内容安全策略)限制,若网页禁止
eval
或内联脚本,可能需要调整manifest.json
的content_security_policy
。 - 避免注入未验证的代码,防止 XSS 风险。
- 注入的脚本受网页的 CSP(内容安全策略)限制,若网页禁止
总结
注入临时脚本的核心作用是“突破上下文隔离”,让代码在网页环境中执行以访问受限制的资源(如 blob)。在你的场景中,通过这种方式可以将网页内的 blob 数据转换为扩展可处理的 data URL,最终实现自定义文件名下载。
如果需要更复杂的命名规则(如根据网页标题、时间戳等),只需修改 newFileName
的生成逻辑即可。
最近更新
- 2025-09-22 01:46
- 可以直接在content.js里面写一个函数获取blob数据实现自定义下载吗?
- 2025-09-22 00:17
- background.js中可通过注入脚本的方法实现获取blobUrl实现自定义下载
- 2025-09-21 17:45
- 通过MutationObserver 开发chrome浏览器扩展的实例
- 2025-09-21 17:23
- MutationObserver实例中mutation.type为childList时addedNodes节点的遍历方法。
- 2025-09-21 17:21
- MutationObserver实例中mutation.type为childList时addedNodes节点的筛选方法。
- 2025-09-21 17:09
- MutationObserver实例中mutation.type为childList时addedNodes节点具体的方法与属性。
- 2025-09-21 16:56
- MutationObserver实例中mutation.type突变类型为childList时有哪些属性、方法?
- 2025-09-21 16:46
- MutationObserver实例中mutation三种不同突变类型的属性与方法?
- 2025-09-21 16:34
- MutationObserver实例中mutation.type突变类型为attributes时有哪些属性、方法?
- 2025-09-15 18:42
- 用豆包图像生成的功能批量创作小说推文的提示词