Chrome扩展Manifest V3实现完整网页下载的问题求助
Chrome扩展Manifest V3下保存网页为MHTML的解决方案
需求
- 实现类似Chrome浏览器Ctrl+S的完整网页保存功能
- 或寻找能将.MHTML文件转换为包含网页文件的ZIP压缩包的库
遇到的问题
在Manifest V3的Service Worker中,URL.createObjectURL()方法未定义,尝试两种解决方案均失败:
- 使用
blobToBase64函数替代URL.createObjectURL(),生成的.MHTML文件打开为空白,无法正常显示 - 将Blob发送给content script处理时,接收的Blob已损坏,调用
URL.createObjectURL()时出现TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed错误,使用FileSaver.js的saveAs()也因Blob损坏而失败
旧参考代码(Manifest V2可用)
// Create new tab, wait until it is loaded and save the page chrome.tabs.create({ url: 'http://example.com' }, function(tab) { chrome.tabs.onUpdated.addListener(function func(tabId, changeInfo) { if (tabId == tab.id && changeInfo.status == 'complete') { chrome.tabs.onUpdated.removeListener(func); savePage(tabId); } }); }); function savePage(tabId) { chrome.pageCapture.saveAsMHTML({ tabId: tabId }, function(blob) { var url = URL.createObjectURL(blob); // Optional: chrome.tabs.remove(tabId); // to close the tab chrome.downloads.download({ url: url, filename: 'whatever.mhtml' }); }); }
失败的尝试代码
第一次尝试(Service Worker中转Base64)
service-worker.js:
function saveAsPage(url) { //Added code start const blobToBase64 = blob => new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(blob); reader.onload = () => resolve(reader.result); reader.onerror = error => reject(error); }); //Added code end function savePage(tabId) { chrome.pageCapture.saveAsMHTML({ tabId: tabId }, async function (blob) { //var url = URL.createObjectURL(blob); //not working in manifest v3 service worker var url = await blobToBase64(blob); //Added line code chrome.downloads.download({ url: url, filename: 'whatever.mhtml' }); }); } chrome.tabs.create({ url: url }, function (tab) { chrome.tabs.onUpdated.addListener(function func(tabId, changeInfo) { if (tabId == tab.id && changeInfo.status == 'complete') { chrome.tabs.onUpdated.removeListener(func); savePage(tabId, tab.title); } }); }); }
第二次尝试(传递Blob到Content Script)
service-worker.js:
function saveAsPage(url) { async function savePage(tabId, tabTitle) { chrome.pageCapture.saveAsMHTML({ tabId: tabId }, async function (blob) { var response = await chrome.tabs.sendMessage(tabId, { blob: blob }); //Added line code chrome.downloads.download({ url: response.url, filename: 'whatever.mhtml' }); }); } chrome.tabs.create({ url: url }, function (tab) { chrome.tabs.onUpdated.addListener(function func(tabId, changeInfo) { if (tabId == tab.id && changeInfo.status == 'complete') { chrome.tabs.onUpdated.removeListener(func); savePage(tabId, tab.title); } }); }); }
content-script.js:
chrome.runtime.onMessage.addListener( function (request, sender, sendResponse) { if (request.blob != undefined) { //saveAS(request.blob); // From FileSaver.js var url = window.URL.createObjectURL(request.blob); sendResponse({ url: url }); } } );
Manifest配置
{ "manifest_version": 3, "name": "Test", "description": "", "version": "0.0.1", "icons": { "16": "logo/logo-16.png", "48": "logo/logo-48.png", "128": "logo/logo-128.png" }, "permissions": [ "activeTab", "pageCapture", "downloads", "tabs", "nativeMessaging" ], "host_permissions": [ "*://*/*" ], "background": { "service_worker": "service-worker.js" }, "content_scripts": [ { "js": [ "FileSaver.js", "content-script.js" ], "matches": [ "*://*/*" ] } ] }
正确解决方案
Manifest V3的chrome.downloads.download()API支持直接传入Blob对象,无需通过URL.createObjectURL()或转Base64。修改savePage函数即可解决问题:
修改后的Service Worker代码
function saveAsPage(url) { function savePage(tabId, tabTitle) { chrome.pageCapture.saveAsMHTML({ tabId: tabId }, function(blob) { // 直接将Blob传入downloads.download的blob参数 chrome.downloads.download({ blob: blob, filename: `${tabTitle || '网页保存'}.mhtml` }); // 可选:关闭新建的标签页 chrome.tabs.remove(tabId); }); } chrome.tabs.create({ url: url }, function(tab) { chrome.tabs.onUpdated.addListener(function func(tabId, changeInfo) { if (tabId === tab.id && changeInfo.status === 'complete') { chrome.tabs.onUpdated.removeListener(func); savePage(tabId, tab.title); } }); }); }
失败原因说明
- Base64转换方案:
chrome.downloads.download的url参数虽然支持Data URL,但MHTML格式的Data URL可能因编码规则或浏览器解析逻辑问题,导致生成的文件无法正常渲染,出现空白。 - Blob传递到Content Script:Chrome的消息传递系统会自动将消息序列化为JSON,Blob对象无法被正确序列化,传递后会丢失原始数据,导致接收的是损坏的对象,进而引发错误。
MHTML转ZIP压缩包的实现思路
若需要将MHTML转换为包含独立网页文件的ZIP包,可以按以下步骤实现:
- 使用
mhtml-parser解析MHTML Blob,提取HTML、CSS、图片等资源 - 使用
jszip创建ZIP包,将解析出的资源逐个添加到包中 - 通过
chrome.downloads.download保存ZIP文件
示例代码(需引入mhtml-parser和jszip)
async function mhtmlToZip(mhtmlBlob) { const mhtmlText = await mhtmlBlob.text(); const parsed = await mhtmlParser.parse(mhtmlText); const zip = new JSZip(); // 添加主HTML文件 zip.file('index.html', parsed.html); // 添加所有资源文件(CSS、图片等) parsed.resources.forEach(res => { zip.file(res.filename, res.content); }); // 生成ZIP Blob并触发下载 const zipBlob = await zip.generateAsync({ type: 'blob' }); chrome.downloads.download({ blob: zipBlob, filename: '网页打包.zip' }); }
内容的提问来源于stack exchange,提问作者MWagihM
相关产品推荐
相关产品推荐

