You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何将设备麦克风的音频流实时上传至Node服务器?

实时音频流上传到Node服务器:WebSocket vs HTTP方案对比

首先直接给结论:如果要实现用户说话同时实时上传的需求,WebSocket是更合适的选择,HTTP方案虽然能实现,但在实时性、性能上有明显短板,下面详细拆解两种方案的优劣和具体实现:

为什么优先选WebSocket?

WebSocket是一种持久化的双向通信协议,和HTTP的请求-响应模式本质不同:

  • 只需要一次握手就能建立长期连接,避免了频繁创建HTTP连接的额外开销
  • 支持实时、低延迟的数据传输,完美适配连续音频流这类高频、不间断的数据传输场景
  • 服务器和客户端可以随时双向发送数据,完全匹配“一边录音一边上传”的实时需求

WebSocket方案具体实现

前端代码(基于你已有的getUserMedia逻辑扩展)

// 你原有的获取音频流代码
const constraints = { 
  video: false, 
  audio: {deviceId: this.state.deviceId ? {exact: this.state.deviceId} : undefined} 
};
window.navigator.mediaDevices.getUserMedia(constraints)
  .then(stream => this.handleStream(stream))
  .catch(err => this.handleError(err));

// 新增的handleStream函数,处理WebSocket上传
handleStream(stream) {
  // 初始化MediaRecorder处理音频流,格式选webm(大部分浏览器原生支持)
  const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });
  // 建立WebSocket连接到你的Node服务器
  const socket = new WebSocket('ws://localhost:8080');

  // 连接成功后开始录音,每100ms生成一个数据块(可根据需求调整间隔)
  socket.onopen = () => {
    console.log('WebSocket连接已建立,开始上传音频');
    mediaRecorder.start(100);
  };

  // 每次生成音频数据块时,通过WebSocket发送给服务器
  mediaRecorder.ondataavailable = (event) => {
    if (event.data.size > 0 && socket.readyState === WebSocket.OPEN) {
      socket.send(event.data);
    }
  };

  // 处理连接关闭和错误
  socket.onclose = () => {
    mediaRecorder.stop();
    console.log('WebSocket连接关闭,停止录音');
  };

  socket.onerror = (error) => {
    console.error('WebSocket连接出错:', error);
    mediaRecorder.stop();
  };
}

Node后端代码(用ws库实现WebSocket服务)

首先安装依赖:npm install ws

const WebSocket = require('ws');
const fs = require('fs');

// 创建WebSocket服务器,监听8080端口
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
  console.log('有客户端连接,开始接收音频流');
  // 创建可写流,用来保存音频文件(追加模式)
  const audioWriteStream = fs.createWriteStream('recorded_audio.webm', { flags: 'a' });

  // 接收前端发送的音频数据块
  ws.on('message', (data) => {
    // data是二进制数据,直接写入文件
    audioWriteStream.write(data);
    console.log('收到音频数据块,大小:', data.length);
  });

  // 客户端断开连接时关闭文件流
  ws.on('close', () => {
    audioWriteStream.end();
    console.log('客户端断开连接,停止接收音频');
  });

  // 处理错误
  ws.on('error', (error) => {
    console.error('WebSocket连接错误:', error);
    audioWriteStream.end();
  });
});

console.log('WebSocket服务器运行在ws://localhost:8080');

HTTP方案的局限性及实现(不推荐实时场景)

HTTP是请求-响应模式,每次上传都需要发起一次独立请求,即使使用HTTP/2的多路复用,也无法避免请求头的开销和延迟,更适合非实时的大文件分块上传,而非实时连续流场景。

HTTP方案示例(仅作参考)

前端代码(用axios发送分块数据)

handleStream(stream) {
  const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });
  // 每500ms生成一个数据块(比WebSocket大,减少请求次数)
  mediaRecorder.start(500);

  mediaRecorder.ondataavailable = async (event) => {
    if (event.data.size > 0) {
      try {
        await axios.post('http://localhost:3000/upload-audio', event.data, {
          headers: {
            'Content-Type': 'audio/webm',
            'Transfer-Encoding': 'chunked'
          }
        });
        console.log('音频块上传成功');
      } catch (err) {
        console.error('音频块上传失败:', err);
      }
    }
  };
}

Node后端代码(用Express处理分块上传)

首先安装依赖:npm install express

const express = require('express');
const fs = require('fs');
const app = express();

// 处理POST请求的分块音频数据
app.post('/upload-audio', (req, res) => {
  const audioWriteStream = fs.createWriteStream('recorded_audio_http.webm', { flags: 'a' });
  
  // 将请求流直接管道到文件流
  req.pipe(audioWriteStream);

  req.on('end', () => {
    res.status(200).send('音频块接收完成');
  });

  req.on('error', (err) => {
    console.error('接收音频块出错:', err);
    res.status(500).send('上传失败');
    audioWriteStream.end();
  });
});

app.listen(3000, () => {
  console.log('HTTP服务器运行在http://localhost:3000');
});

最终建议

如果你的核心需求是实时同步上传音频流,一定要选WebSocket方案,它的低延迟和持久连接特性完全匹配场景;如果只是需要把录音文件上传(非实时),HTTP方案就足够了。

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

火山引擎 最新活动