Manifest V3版Chrome扩展将远程响应传递给content script实现文件另存为问题
问题根源
chrome.scripting.executeScript的参数传递需要经过跨进程序列化,Blob类型无法被正常序列化传递,注入到页面后拿到的实际是普通对象,调用toString()后就变成了[object Object],导致保存的内容错误。
最优解决方案
直接使用Manifest V3原生支持的chrome.downloads API完成下载,不需要注入脚本到页面执行,步骤更简单,也不存在跨进程传值问题:
- 先在
manifest.json中声明必要权限:
{ "manifest_version": 3, "permissions": ["activeTab", "scripting", "downloads"], "host_permissions": ["http://localhost:5000/*"], // 其余扩展配置保持不变 }
- 修改后台service worker代码如下:
// 点击扩展图标时触发 chrome.action.onClicked.addListener(async tab => { if (!tab.url.startsWith('http')) return try { const res = await post({url: tab.url}) const filename = getFilename(res.headers.get('Content-Disposition')) const blob = await res.blob() const blobUrl = URL.createObjectURL(blob) // 直接调用下载API,saveAs参数会唤起系统保存对话框 await chrome.downloads.download({ url: blobUrl, filename: filename, saveAs: true }) URL.revokeObjectURL(blobUrl) } catch (error) { await chrome.scripting.executeScript({ target: {tabId: tab.id}, func: showAlert, args: [error], }) } }); function getFilename(header) { return /filename="(.+)"/.exec(header)[1]; } async function post(data = {}) { return await fetch('http://localhost:5000/citation',{ method: 'POST', body: new URLSearchParams(data) }); } function showAlert(error) { let message = error.error !== null ? error.error : error.message; alert("Error: " + message); }
替代方案(需保留原注入脚本逻辑时使用)
如果必须通过注入页面脚本的方式下载,可以先把Blob转成Base64字符串再传递,字符串可正常跨进程序列化:
- 后台传递Base64字符串:
// 拿到blob后转base64 const buffer = await blob.arrayBuffer() const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer))) // 注入脚本时传递base64而非blob chrome.scripting.executeScript({ target: {tabId: tab.id}, func: saveFile, args: [response.filename, base64], })
- 页面侧
saveFile把Base64转回Blob:
async function saveFile(filename, base64) { const binary = atob(base64) const array = [] for(let i = 0; i < binary.length; i++) { array.push(binary.charCodeAt(i)) } const blob = new Blob([new Uint8Array(array)], {type: 'application/octet-stream'}) let link = document.createElement('a'); let url = window.URL.createObjectURL(blob); link.href = url; link.download = filename; link.click(); setTimeout(() => { window.URL.revokeObjectURL(url); }, 250); }
内容的提问来源于stack exchange,提问作者menteith
相关产品推荐
相关产品推荐

