Chrome扩展中使用XMLHttpRequest/Fetch Polyfill上传图片至Firebase的问题
解决Chrome扩展中Firebase Storage截图上传的ReferenceError问题
问题根源
你遇到的ReferenceError核心原因有两点:
chrome.tabs.captureVisibleTab返回的是base64格式的Data URL,而Firebase SDK的uploadBytes要求传入Blob/File对象,直接传入Data URL会导致类型不匹配错误。- Chrome扩展的背景脚本/service worker环境对Firebase Storage SDK的部分API支持有限,改用REST API通过Fetch/XHR上传更稳定。
解决方案步骤
1. 将Data URL转换为Blob对象
首先需要把截图的Data URL转换成Blob,这是上传二进制数据的必要前提:
function dataURLtoBlob(dataUrl) { const arr = dataUrl.split(','); const mime = arr[0].match(/:(.*?);/)[1]; const bstr = atob(arr[1]); let n = bstr.length; const u8arr = new Uint8Array(n); while(n--) { u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], {type: mime}); }
2. 获取Firebase Auth授权Token
Firebase Storage的REST API需要授权,通过Firebase Auth获取当前用户的ID Token即可:
async function getIdToken() { const user = firebase.auth().currentUser; if (!user) { throw new Error('用户未登录,无法完成上传授权'); } return await user.getIdToken(); }
3. 使用Fetch上传到Firebase Storage
替换你原有的getScreenshot函数,用Fetch实现完整上传流程:
async function getScreenshot() { chrome.tabs.captureVisibleTab(async (dataUrl) => { try { // 转换截图为Blob const blob = dataURLtoBlob(dataUrl); // 生成唯一存储路径,避免文件名重复 const ssPathName = `sessionSS/${Date.now()}_${Math.random().toString(36).slice(2)}`; // 获取授权Token const idToken = await getIdToken(); // 构造Firebase Storage上传接口地址 const storageBucket = firebase.app().options.storageBucket; const uploadUrl = `https://firebasestorage.googleapis.com/v0/b/${storageBucket}/o?name=${encodeURIComponent(ssPathName)}`; // 发送上传请求 const response = await fetch(uploadUrl, { method: 'POST', headers: { 'Authorization': `Bearer ${idToken}`, 'Content-Type': blob.type }, body: blob }); if (!response.ok) { throw new Error(`上传失败: ${await response.text()}`); } const result = await response.json(); // 构造截图的下载URL(可直接存入Firestore) const downloadUrl = `https://firebasestorage.googleapis.com/v0/b/${storageBucket}/o/${encodeURIComponent(ssPathName)}?alt=media`; console.log('截图上传成功', downloadUrl); // 将下载URL写入session日志 await session.add({ screenshotUrl: downloadUrl, timestamp: new Date(), // 补充你的其他session字段 }); } catch (error) { console.error('截图上传失败', error); } }); }
4. 修复异步逻辑问题(可选但关键)
你的getBlackList是异步函数,但原代码中直接调用后就遍历blackList,此时黑名单数据可能还未加载完成,需要调整为异步等待:
chrome.tabs.query({ active: true, lastFocusedWindow: true }, async (tab) => { let url = tab[0].url; let title = tab[0].title; // 等待黑名单加载完成 await getBlackList(); eventTimeStamp = new Date(); await event.add({ isIncognito: tab[0].incognito, // 原代码tab.incognito应为tab[0].incognito // 补充你的其他event字段 }); for (let i = 0; i < blackList.length; i++) { if (url === blackList[i].samplesite) { setTimeout(() => { getScreenshot(); }, 100); sessionTimeStamp = new Date(); await session.add({ // 补充你的其他session字段 }); // SMS API逻辑 } } });
额外注意事项
- 确保Firebase Storage规则允许当前用户上传文件,示例规则:
service firebase.storage { match /b/{bucket}/o { match /sessionSS/{allPaths=**} { allow write: if request.auth != null; allow read: if request.auth != null; } } } - 用时间戳+随机字符串作为存储路径,能有效避免文件名冲突。
内容的提问来源于stack exchange,提问作者Carlo A
相关产品推荐
相关产品推荐

