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

Chrome扩展Socket.IO文件传输异常:无法接收原始文件

解决Chrome扩展中Socket.IO文件共享的Blob异常问题

问题根源

Chrome扩展的跨上下文消息传递机制(比如chrome.runtime.sendMessage或chrome.runtime.connect)会自动序列化传递的数据,Uint8Array这类二进制类型会被转换成普通的JSON对象(而非原始二进制数据)。当你在popup把Uint8Array发给background时,实际传递的是一个包含byteLength、buffer等字段的对象,而非真正的二进制数组。后续服务器转发、background接收拼接时,处理的是这些对象的字符串形式,最终生成的Blob自然不对,表现为文件内容是[object Object],大小也异常。

修复方案

1. Popup端:将Uint8Array转为Base64字符串传递

在popup.js中,不要直接发送Uint8Array,而是先转成Base64字符串,避免序列化失真:

// popup.js 读取文件并发送
document.getElementById('file-input').addEventListener('change', async (e) => {
  const file = e.target.files[0];
  const arrayBuffer = await file.arrayBuffer();
  const uint8Array = new Uint8Array(arrayBuffer);
  // 转成Base64字符串
  const base64 = btoa(String.fromCharCode(...uint8Array));
  // 发送给background
  chrome.runtime.sendMessage({
    type: 'file-data',
    filename: file.name,
    data: base64,
    size: file.size
  });
});

2. Background端:将Base64转回Uint8Array再转发

background.js收到消息后,先把Base64转回Uint8Array,再通过Socket.IO发给服务器:

// background.js 接收并转发
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'file-data') {
    // Base64转回Uint8Array
    const binaryStr = atob(message.data);
    const uint8Array = new Uint8Array(binaryStr.length);
    for (let i = 0; i < binaryStr.length; i++) {
      uint8Array[i] = binaryStr.charCodeAt(i);
    }
    // 通过Socket.IO发送给服务器
    socket.emit('file-chunk', {
      filename: message.filename,
      data: uint8Array,
      totalSize: message.size
    });
  }
});

3. 服务器端:保持原有的分块逻辑(无需修改)

服务器的分块发送逻辑可以和Web版一致,因为Socket.IO在Node.js环境中可以正确处理Uint8Array类型的二进制数据。

4. 接收端Background:正确拼接分块为Blob

接收分块时,确保每一块都是Uint8Array类型,再收集后生成Blob:

// background.js 接收服务器分块并拼接
const fileChunks = new Map(); // key: filename, value: { chunks: [], totalSize: number }

socket.on('receive-chunk', (data) => {
  const { filename, chunk, totalSize } = data;
  if (!fileChunks.has(filename)) {
    fileChunks.set(filename, { chunks: [], totalSize });
  }
  // 确保chunk是Uint8Array(如果服务器转发时类型正确,这里无需额外处理)
  fileChunks.get(filename).chunks.push(chunk);
  
  // 检查是否收完所有分块
  const receivedSize = fileChunks.get(filename).chunks.reduce((sum, chunk) => sum + chunk.length, 0);
  if (receivedSize === totalSize) {
    // 拼接所有Uint8Array为一个大的Uint8Array
    const combined = new Uint8Array(receivedSize);
    let offset = 0;
    for (const chunk of fileChunks.get(filename).chunks) {
      combined.set(chunk, offset);
      offset += chunk.length;
    }
    // 生成Blob
    const blob = new Blob([combined], { type: 'application/octet-stream' });
    // 触发下载
    const url = URL.createObjectURL(blob);
    chrome.downloads.download({ url, filename });
    // 清理缓存
    fileChunks.delete(filename);
  }
});

替代方案:使用Chrome扩展的二进制消息传递

如果不想用Base64(避免额外的体积开销),可以使用chrome.runtime.connect建立长连接,通过postMessage传递ArrayBuffer类型的二进制数据,因为长连接支持直接传递二进制:

// popup.js 建立长连接并发送ArrayBuffer
const port = chrome.runtime.connect({ name: 'file-transfer' });
port.postMessage({ type: 'file-data', filename: file.name, size: file.size }, [arrayBuffer]);

// background.js 监听长连接
chrome.runtime.onConnect.addListener((port) => {
  if (port.name === 'file-transfer') {
    port.onMessage.addListener((message, port) => {
      if (message.type === 'file-data') {
        // port.postMessage的第二个参数是Transferable,这里arrayBuffer会被转移
        const uint8Array = new Uint8Array(port.sender.data);
        socket.emit('file-chunk', { filename: message.filename, data: uint8Array, totalSize: message.size });
      }
    });
  }
});

这种方式更高效,适合大文件传输,因为不需要Base64编码的额外开销。

内容的提问来源于stack exchange,提问作者SHREY RAJ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 22:27:25