如何将含Blob数据的JavaScript对象序列化后通过WebSocket发送?
解决WebSocket发送包含Blob的序列化消息问题
直接发送Blob给WebSocket能正常工作,但用JSON.stringify封装Blob会失败,核心原因是JSON无法序列化Blob对象——Blob属于浏览器特有的二进制对象,JSON序列化时只会保留其原型链上的可枚举属性,最终得到{ type: 'REC', data: {} }这种无效结构,服务器自然无法解析。
下面提供三种可行的解决方案,按实现复杂度和效率排序:
方案1:将Blob转为Base64字符串封装到JSON
这是最直观的方法,把Blob转成Base64编码的字符串,就能被JSON正常序列化。
前端代码
recorder = new MediaRecorder(canvasStream); recorder.ondataavailable = e => { const reader = new FileReader(); // 读取Blob为DataURL格式 reader.onload = function() { // 剔除DataURL前缀(如"data:video/webm;base64,"),只保留Base64内容 const base64Data = reader.result.split(',')[1]; // 封装成JSON对象发送 ws.send(JSON.stringify({ type: 'REC', data: base64Data })); }; reader.readAsDataURL(e.data); };
服务器端处理(以Node.js为例)
接收后解析JSON,再将Base64转回二进制:
ws.on('message', (message) => { const msg = JSON.parse(message); if (msg.type === 'REC') { // 将Base64转为Buffer(二进制数据) const blobBuffer = Buffer.from(msg.data, 'base64'); // 后续处理逻辑... } });
优缺点:实现简单,无需额外依赖;但Base64会让数据体积增加约33%,适合小体积的媒体片段。
方案2:自定义二进制帧(高效无体积损耗)
直接使用WebSocket的二进制发送能力,将消息类型和Blob数据打包成一个二进制帧,避免Base64的体积开销。
前端代码
recorder = new MediaRecorder(canvasStream); recorder.ondataavailable = e => { const typeStr = 'REC'; // 将类型字符串转为Uint8Array const typeEncoder = new TextEncoder(); const typeUint8 = typeEncoder.encode(typeStr); // 读取Blob的ArrayBuffer e.data.arrayBuffer().then(blobBuffer => { // 计算总缓冲区大小:4字节存储类型长度 + 类型内容长度 + Blob数据长度 const totalLength = 4 + typeUint8.length + blobBuffer.byteLength; const totalBuffer = new ArrayBuffer(totalLength); const dataView = new DataView(totalBuffer); // 写入类型长度(32位无符号整数,小端字节序) dataView.setUint32(0, typeUint8.length, true); // 写入类型内容 new Uint8Array(totalBuffer, 4, typeUint8.length).set(typeUint8); // 写入Blob二进制数据 new Uint8Array(totalBuffer, 4 + typeUint8.length).set(new Uint8Array(blobBuffer)); // 发送二进制帧 ws.send(totalBuffer); }); };
服务器端处理(以Node.js为例)
解析二进制帧,先读取类型,再提取Blob数据:
ws.on('message', (buffer) => { const dataView = new DataView(buffer.buffer); // 读取前4字节得到类型长度 const typeLength = dataView.getUint32(0, true); // 读取类型字符串 const typeStr = new TextDecoder().decode(new Uint8Array(buffer.buffer, 4, typeLength)); if (typeStr === 'REC') { // 提取Blob数据 const blobBuffer = buffer.slice(4 + typeLength); // 后续处理逻辑... } });
优缺点:数据无体积损耗,传输效率高;需要手动处理二进制编解码,逻辑稍复杂。
方案3:使用支持二进制的序列化库(如MessagePack)
MessagePack是一种高效的二进制序列化格式,原生支持二进制数据,比JSON更紧凑,且无需手动处理编码。
前端代码(需引入msgpack-lite库)
import msgpack from 'msgpack-lite'; recorder = new MediaRecorder(canvasStream); recorder.ondataavailable = async e => { // 将Blob转为Uint8Array const blobBuffer = new Uint8Array(await e.data.arrayBuffer()); // 序列化包含类型和数据的对象 const packedData = msgpack.encode({ type: 'REC', data: blobBuffer }); // 发送二进制数据 ws.send(packedData); };
服务器端处理(Node.js)
const msgpack = require('msgpack-lite'); ws.on('message', (buffer) => { const msg = msgpack.decode(buffer); if (msg.type === 'REC') { // msg.data 就是Uint8Array格式的二进制数据 const blobBuffer = Buffer.from(msg.data); // 后续处理逻辑... } });
优缺点:兼顾易用性和效率,无需手动处理二进制编解码;但需要前后端引入相同的序列化库。
内容的提问来源于stack exchange,提问作者Hypothesis
相关产品推荐
相关产品推荐

