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

麦克风录制Blob分块流式上传后端:动态添加Blob至流的实现问询

实现边录边传音频(逐Blob流式发送)

核心思路

利用浏览器的MediaRecorder分段获取音频Blob,通过自定义ReadableStream将Blob持续注入流中,再用Axios把这个流作为请求体发送给后端——这样就能在用户结束录制前就开始传输数据,节省等待时间。

具体实现步骤

1. 初始化媒体录制

先获取用户麦克风权限,创建MediaRecorder实例,监听ondataavailable事件来获取分段Blob:

// 存储待发送的Blob队列
const blobQueue = [];
let mediaRecorder;

// 获取麦克风流并初始化录制
async function startRecording() {
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
  mediaRecorder = new MediaRecorder(stream);

  // 每段数据可用时,加入队列
  mediaRecorder.ondataavailable = (e) => {
    if (e.data.size > 0) {
      blobQueue.push(e.data);
    }
  };

  mediaRecorder.start(1000); // 每1秒生成一个Blob(可根据需求调整间隔)
}

2. 创建可动态添加Blob的ReadableStream

自定义一个ReadableStream,它会从blobQueue中取出Blob,转换成二进制数据后推送给流:

function createBlobStream() {
  return new ReadableStream({
    async pull(controller) {
      // 如果队列里有Blob,就取出处理
      if (blobQueue.length > 0) {
        const blob = blobQueue.shift();
        const arrayBuffer = await blob.arrayBuffer();
        const uint8Array = new Uint8Array(arrayBuffer);
        controller.enqueue(uint8Array);
      } else if (mediaRecorder.state === 'inactive') {
        // 录制已结束且队列空,关闭流
        controller.close();
      } else {
        // 队列空但还在录制,等待100ms再检查
        await new Promise(resolve => setTimeout(resolve, 100));
        this.pull(controller);
      }
    },
    cancel() {
      // 取消时清空队列
      blobQueue.length = 0;
    }
  });
}

3. 用Axios发送流式请求

Axios支持将ReadableStream作为请求体(需确保浏览器/后端支持流式传输),直接发起POST请求:

async function startStreamingUpload() {
  const stream = createBlobStream();
  try {
    await axios.post('/api/upload-audio', stream, {
      headers: {
        'Content-Type': 'audio/webm', // 对应MediaRecorder的输出格式,可根据实际调整
        'Transfer-Encoding': 'chunked' // 告诉后端这是分块传输
      },
      // 禁用Axios的自动转换,直接传递流
      transformRequest: [(data) => data]
    });
    console.log('音频上传完成');
  } catch (error) {
    console.error('上传失败:', error);
    // 失败时停止录制
    mediaRecorder.stop();
  }
}

4. 控制录制与上传的流程

// 开始录制+启动上传
async function start() {
  await startRecording();
  startStreamingUpload();
}

// 结束录制
function stopRecording() {
  if (mediaRecorder && mediaRecorder.state !== 'inactive') {
    mediaRecorder.stop();
  }
}

注意事项

  • 后端兼容性:后端需要支持分块传输(Transfer-Encoding: chunked),并能接收流式的音频数据进行拼接存储。
  • 格式一致性:确保MediaRecorder的输出格式(如audio/webm)和请求头的Content-Type一致,后端也要能处理该格式。
  • 错误处理:如果上传中断,要及时停止录制并清理资源;同时要处理用户拒绝麦克风权限的情况。
  • 浏览器支持:ReadableStream和MediaRecorder在现代浏览器中都已支持,但需注意旧版浏览器的兼容性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 08:27:28