如何通过Node.js正确保存Socket.IO传输的MediaRecorder OGG音频Blob?
问题
在浏览器端通过MediaRecorder录制音频,停止录制后将Blob格式的音频数据通过Socket.IO上传至Node.js服务并写入文件,上传流程正常,但服务器生成的文件并非有效的音频文件。
客户端传输代码:
recorder.ondataavailable = async (chunk) => { console.log(`Mime type: ${recorder.mimeType}`); console.log(`blowing chunks`); chunks.value.push(chunk.data); const result = await socket.emitWithAck('data', chunk.data); console.log(result); };
服务端接收代码:
socket.on('data', async (data: Buffer, ack) => { try { writeFileSync('data/testfile.ogg', data); ack('saved'); } catch (err) { console.log(err); // throw (err); } });
补充说明:
- 曾尝试使用
appendFile,但客户端ondataavailable仅在停止录制时触发,所以write和append效果一致,用write还省去手动删除文件的操作。 - 已知音频媒体类型为
audio/ogg,且客户端与服务端的数据大小一致,数据未丢失。
请问通过Node.js正确保存编码后的OGG音频二进制数据的方法是什么?
解决方案
问题出在Socket.IO传输Blob数据时的格式转换上:浏览器端的chunk.data是Blob对象,直接通过Socket.IO传输会被自动序列化为JSON对象,而非原始二进制数据,导致服务端收到的Buffer是序列化后的结果,并非真正的音频二进制流。
解决步骤如下:
客户端:将Blob转换为ArrayBuffer再传输
发送前把Blob转成ArrayBuffer,确保传输的是原始二进制数据:recorder.ondataavailable = async (chunk) => { console.log(`Mime type: ${recorder.mimeType}`); console.log(`blowing chunks`); chunks.value.push(chunk.data); // 将Blob转为ArrayBuffer const arrayBuffer = await chunk.data.arrayBuffer(); const result = await socket.emitWithAck('data', arrayBuffer); console.log(result); };服务端:直接写入ArrayBuffer对应的Buffer
Socket.IO会将ArrayBuffer转换为Node.js的Buffer,此时直接写入文件即可:import { writeFileSync } from 'fs'; import { join } from 'path'; socket.on('data', async (data: Buffer, ack) => { try { // 确保目录存在,避免写入失败 const filePath = join(__dirname, 'data', 'testfile.ogg'); writeFileSync(filePath, data, { flag: 'w' }); ack('saved'); } catch (err) { console.error('写入文件失败:', err); ack('error'); } });额外验证:合并多段数据(如果后续ondataavailable多次触发)
若后续录制过程中ondataavailable会多次触发(比如设置了timeslice),则需要用appendFileSync累加数据,或者先收集所有数据再一次性写入:// 服务端累加数据示例 let audioBuffer = Buffer.from([]); socket.on('data', (data: Buffer, ack) => { audioBuffer = Buffer.concat([audioBuffer, data]); ack('chunk received'); }); socket.on('stop', () => { writeFileSync('data/testfile.ogg', audioBuffer); audioBuffer = Buffer.from([]); // 重置缓冲区 });
内容的提问来源于stack exchange,提问作者Stuart Grimshaw
相关产品推荐
相关产品推荐

