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

实现将音频Blob持续加载至POST请求上传至Node.js的技术问询

我来帮你理清这个流式上传的实现思路——其实核心是把音频录制的数据流分段实时传输到后端,而不是等录制完再传整个大Blob。这样录制过程中就开始上传,结束后很快就能完成后续处理,完美解决上传耗时久的问题。我分客户端(React)和服务端(Node.js)两部分给你拆解具体实现:

客户端(React):流式录制+实时上传

你用的RecordRTC本身就支持分段生成音频Blob,我们可以利用它的timeSlice配置和onDataAvailable回调,把录制的小Blob块实时推送到后端的流式请求里:

import RecordRTC from 'recordrtc';

let recorder;
let uploadWriter;

// 开始录制并启动流式上传
const startRecording = async () => {
  // 获取麦克风权限
  const audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });

  // 配置RecordRTC:每1秒生成一个音频片段
  recorder = new RecordRTC(audioStream, {
    type: 'audio',
    mimeType: 'audio/webm', // 优先用webm格式,体积更小
    timeSlice: 1000, // 每1000ms触发一次onDataAvailable
    onDataAvailable: (blob) => {
      // 把片段写入上传流(过滤空Blob)
      if (uploadWriter && blob.size > 0) {
        uploadWriter.write(blob);
      }
    }
  });

  // 创建自定义可读流,用来传递录制的音频片段
  const uploadStream = new ReadableStream({
    start(controller) {
      uploadWriter = controller;
    },
    cancel() {
      console.log('上传流已终止');
    }
  });

  // 发起流式POST请求到后端
  fetch('/api/upload-audio', {
    method: 'POST',
    body: uploadStream,
    headers: {
      'Content-Type': 'audio/webm' // 和录制格式保持一致
    }
  })
  .then(res => res.json())
  .then(data => {
    console.log('上传&处理完成!', data);
    // 这里可以展示处理后的音频给用户
  })
  .catch(err => console.error('上传出错:', err));

  // 启动录制
  recorder.startRecording();
};

// 停止录制并结束上传流
const stopRecording = () => {
  recorder.stopRecording(() => {
    // 关闭上传流,告诉后端数据已全部发送
    if (uploadWriter) {
      uploadWriter.close();
    }
  });
};

如果RecordRTC的分段回调不好用,也可以切换到原生MediaRecorder(API更稳定),逻辑完全一致,只是初始化方式不同:

// 原生MediaRecorder替代方案
const startRecording = async () => {
  const audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });
  const mediaRecorder = new MediaRecorder(audioStream, { mimeType: 'audio/webm' });
  
  // 后续的上传流创建、请求发起逻辑和上面一致
  mediaRecorder.ondataavailable = (e) => {
    if (uploadWriter && e.data.size > 0) {
      uploadWriter.write(e.data);
    }
  };
  mediaRecorder.start(1000); // 每1秒生成一个片段
};
服务端(Node.js):接收流式上传并处理

Node.js的req对象本身就是可读流,我们不需要用body-parser(它会把整个请求体读入内存),直接把请求流pipe到文件流里,后续再做音频处理:

const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const ffmpeg = require('fluent-ffmpeg'); // 可选,用来转码/压缩音频

// 确保uploads目录存在
const uploadDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir);

app.post('/api/upload-audio', (req, res) => {
  // 生成唯一文件名
  const rawFilename = `raw-audio-${Date.now()}.webm`;
  const rawFilePath = path.join(uploadDir, rawFilename);
  
  // 创建文件可写流,接收上传的音频片段
  const writeStream = fs.createWriteStream(rawFilePath);
  
  // 把请求流pipe到文件流
  req.pipe(writeStream);
  
  // 监听上传完成事件
  writeStream.on('finish', async () => {
    try {
      // --- 这里可以加音频处理逻辑(比如转成mp3、压缩)---
      const processedFilename = `processed-audio-${Date.now()}.mp3`;
      const processedFilePath = path.join(uploadDir, processedFilename);
      
      await new Promise((resolve, reject) => {
        ffmpeg(rawFilePath)
          .toFormat('mp3')
          .audioBitrate('64k') // 降低比特率压缩
          .save(processedFilePath)
          .on('end', resolve)
          .on('error', reject);
      });
      
      // 返回处理后的音频地址给客户端
      res.json({
        success: true,
        audioUrl: `/uploads/${processedFilename}`
      });
      
      // 可选:删除原始webm文件节省空间
      fs.unlinkSync(rawFilePath);
    } catch (err) {
      console.error('音频处理出错:', err);
      res.status(500).json({ success: false, message: '音频处理失败' });
    }
  });
  
  // 监听上传错误
  writeStream.on('error', (err) => {
    console.error('文件写入出错:', err);
    res.status(500).json({ success: false, message: '上传失败' });
  });
});

// 静态文件服务,让客户端可以访问处理后的音频
app.use('/uploads', express.static(uploadDir));

app.listen(3000, () => {
  console.log('服务运行在 http://localhost:3000');
});
关键注意事项
  1. 格式一致性:客户端录制的MIME类型要和服务端接收、处理的格式统一(比如都用webm),否则拼接后的文件可能无法播放。
  2. 错误处理:要处理录制中断、上传中断的情况(比如用户刷新页面),及时关闭流、清理临时文件。
  3. 音频处理工具:如果需要转码、压缩,推荐用fluent-ffmpeg(需要先安装ffmpeg本体),它能轻松把webm转成mp3、调整比特率。
  4. 跨域问题:如果前端和后端不在同一个域名,要在Node.js服务端配置CORS。

内容的提问来源于stack exchange,提问作者Kylo.Bear

火山引擎 最新活动