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

如何将录音生成的Blob转为MP3并通过POST请求发送?

解决WebM/Opus Blob转MP3并上传的问题

问题本质

你录制生成的是audio/webm;codecs=opus格式的Blob,浏览器没有原生API直接将其转为MP3,必须借助第三方音频编码库实现格式转换,再通过FormData上传。

方案一:使用lamejs(轻量MP3编码库)

lamejs是专门针对MP3编码的轻量级库,适合简单的音频转码场景。

步骤1:解码WebM为PCM数据

首先需要将WebM Blob解码成PCM(脉冲编码调制)原始音频数据,这是编码MP3的前提:

async function webmToPcm(blob) {
  const audioContext = new (window.AudioContext || window.webkitAudioContext)();
  const arrayBuffer = await blob.arrayBuffer();
  const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
  
  // 转换为单声道数据(lamejs默认支持单声道,也可扩展为立体声)
  const channelData = audioBuffer.getChannelData(0);
  // 将32位浮点PCM转为16位整数PCM(lamejs要求的输入格式)
  const pcmData = new Int16Array(channelData.length);
  for (let i = 0; i < channelData.length; i++) {
    const sample = Math.max(-1, Math.min(1, channelData[i]));
    pcmData[i] = sample < 0 ? sample * 0x8000 : sample * 0x7FFF;
  }
  
  return {
    pcm: pcmData,
    sampleRate: audioBuffer.sampleRate
  };
}

步骤2:将PCM编码为MP3 Blob

使用lamejs将PCM数据编码为MP3格式:

function pcmToMp3(pcmData, sampleRate) {
  // 初始化编码器:单声道、采样率、比特率(128kbps)
  const mp3Encoder = new lamejs.Mp3Encoder(1, sampleRate, 128);
  const mp3Chunks = [];
  
  // 按lamejs推荐的chunk大小分批编码
  const chunkSize = 1152;
  for (let i = 0; i < pcmData.length; i += chunkSize) {
    const chunk = pcmData.subarray(i, i + chunkSize);
    const mp3Buf = mp3Encoder.encodeBuffer(chunk);
    if (mp3Buf.length > 0) {
      mp3Chunks.push(new Uint8Array(mp3Buf));
    }
  }
  
  // 完成编码,获取剩余数据
  const endBuf = mp3Encoder.flush();
  if (endBuf.length > 0) {
    mp3Chunks.push(new Uint8Array(endBuf));
  }
  
  // 合并chunk为MP3 Blob
  return new Blob(mp3Chunks, { type: 'audio/mpeg' });
}

步骤3:整合上传逻辑

将转换后的MP3 Blob通过FormData上传:

async function sendBlob() {
  const webmBlob = getBlob; // 替换为你录制得到的Blob对象
  try {
    // 转码为MP3
    const pcmInfo = await webmToPcm(webmBlob);
    const mp3Blob = pcmToMp3(pcmInfo.pcm, pcmInfo.sampleRate);
    
    // 构造FormData
    const formData = new FormData();
    // 第三个参数为文件名,可选但建议添加,方便后端识别
    formData.append("audio-file", mp3Blob, "recording.mp3");
    
    // 发送请求
    const res = await fetch("/api/whisper", {
      method: "POST",
      body: formData,
    });
    const { result } = await res.json();
    console.log('上传成功:', result);
  } catch (error) {
    console.error('转码或上传失败:', error);
  }
}

方案二:使用ffmpeg.wasm(全功能多媒体转码)

如果需要支持更多格式或复杂转码需求,可以使用ffmpeg.wasm,它是FFmpeg的WebAssembly版本,功能更全面但体积较大。

代码示例

// 先通过npm安装:npm install @ffmpeg/ffmpeg @ffmpeg/core
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';

// 初始化FFmpeg实例
const ffmpeg = createFFmpeg({ log: false });

async function webmToMp3(blob) {
  // 加载FFmpeg核心(首次加载会下载核心文件,建议提前加载)
  if (!ffmpeg.isLoaded()) {
    await ffmpeg.load();
  }
  
  // 将WebM Blob写入FFmpeg内存文件系统
  ffmpeg.FS('writeFile', 'input.webm', await fetchFile(blob));
  // 执行转码命令:将WebM转为128kbps的MP3
  await ffmpeg.run('-i', 'input.webm', '-b:a', '128k', 'output.mp3');
  
  // 读取转码后的MP3文件
  const mp3Data = ffmpeg.FS('readFile', 'output.mp3');
  // 转为MP3 Blob
  return new Blob([mp3Data.buffer], { type: 'audio/mpeg' });
}

// 上传函数
async function sendBlob() {
  const webmBlob = getBlob;
  try {
    const mp3Blob = await webmToMp3(webmBlob);
    const formData = new FormData();
    formData.append("audio-file", mp3Blob, "recording.mp3");
    
    const res = await fetch("/api/whisper", {
      method: "POST",
      body: formData,
    });
    const { result } = await res.json();
    console.log('上传成功:', result);
  } catch (error) {
    console.error('转码或上传失败:', error);
  }
}

注意事项

  • 浏览器限制:AudioContext需要在用户交互(如点击、触摸)后初始化,确保你的录制功能是在用户触发的事件中启动的,否则会报错。
  • 性能考量:前端转码会消耗CPU资源,短音频(如语音消息)没问题,长音频建议考虑后端转码(如果允许将WebM上传到后端再转MP3)。
  • 库的引入:lamejs可以通过下载本地脚本引入,ffmpeg.wasm建议通过npm安装或使用本地资源,避免外链依赖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 15:55:33