You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 01:01:01