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

Vue.js消息项目音频Blob转MP3遇问题,求可靠转码方案

Vue项目中音频转MP3的可靠解决方案

一、搞定低版本FFmpeg的SharedArrayBuffer报错

这个错误是浏览器对SharedArrayBuffer有跨源隔离要求导致的,只需在Vue项目的开发服务器配置里加两个响应头:

  • Vite项目:修改vite.config.js
export default defineConfig({
  server: {
    headers: {
      'Cross-Origin-Opener-Policy': 'same-origin',
      'Cross-Origin-Embedder-Policy': 'require-corp'
    }
  }
})
  • Vue CLI项目:修改vue.config.js
module.exports = {
  devServer: {
    headers: {
      'Cross-Origin-Opener-Policy': 'same-origin',
      'Cross-Origin-Embedder-Policy': 'require-corp'
    }
  }
}

改完重启服务,SharedArrayBuffer就能正常使用了。

二、用新版FFmpeg解决编译报错

高版本编译失败是因为@ffmpeg/core的文件结构变更,直接安装适配性较好的新版本即可:

  1. 安装命令:
npm install @ffmpeg/ffmpeg@0.20.4 @ffmpeg/core@0.20.1
  1. 完整Vue组件示例:
<template>
  <div>
    <button @click="startRecording">开始录制</button>
    <button @click="stopRecording">停止录制</button>
    <div id="waveform"></div>
    <audio ref="audioPlayer" controls></audio>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import WaveSurfer from 'wavesurfer.js'
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'

const wavesurfer = ref(null)
const audioPlayer = ref(null)
const ffmpeg = createFFmpeg({ log: true })

onMounted(async () => {
  // 加载FFmpeg核心
  await ffmpeg.load()
  // 初始化波形录制组件
  wavesurfer.value = WaveSurfer.create({
    container: '#waveform',
    waveColor: '#4F4A85',
    progressColor: '#383351',
    barWidth: 2,
    barRadius: 3,
    cursorWidth: 1,
    height: 100,
    barGap: 2
  })
})

const startRecording = () => {
  wavesurfer.value.startRecording()
}

const stopRecording = async () => {
  const blob = await wavesurfer.value.stopRecording()
  // 转码为MP3
  await transcodeToMP3(blob)
}

const transcodeToMP3 = async (inputBlob) => {
  const inputExt = inputBlob.type.split('/')[1]
  const inputFileName = `input.${inputExt}`
  const outputFileName = 'output.mp3'

  // 将录制的Blob写入FFmpeg虚拟文件系统
  ffmpeg.FS('writeFile', inputFileName, await fetchFile(inputBlob))
  // 执行转码命令:libmp3lame为MP3编码器,-q:a 2代表高质量压缩
  await ffmpeg.run('-i', inputFileName, '-codec:a', 'libmp3lame', '-q:a', '2', outputFileName)
  
  // 读取转码后的MP3文件
  const mp3Data = ffmpeg.FS('readFile', outputFileName)
  const mp3Blob = new Blob([mp3Data.buffer], { type: 'audio/mp3' })
  
  // 预览转好的音频
  audioPlayer.value.src = URL.createObjectURL(mp3Blob)
  
  // 可在此添加上传逻辑,示例:
  // await uploadMP3(mp3Blob)
}

// 音频上传示例(可选)
const uploadMP3 = async (mp3Blob) => {
  const formData = new FormData()
  formData.append('audio', mp3Blob, 'record.mp3')
  try {
    await fetch('/api/upload-audio', {
      method: 'POST',
      body: formData
    })
    console.log('上传成功')
  } catch (err) {
    console.error('上传失败', err)
  }
}
</script>

<style scoped>
#waveform {
  margin: 20px 0;
}
</style>

三、无FFmpeg依赖方案:lamejs纯前端转码

嫌FFmpeg配置麻烦?试试lamejs,纯JS实现MP3编码,无需依赖WebAssembly:

  1. 安装命令:
npm install lamejs
  1. 组件示例:
<template>
  <div>
    <button @click="startRecording">开始录制</button>
    <button @click="stopRecording">停止录制</button>
    <div id="waveform"></div>
    <audio ref="audioPlayer" controls></audio>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import WaveSurfer from 'wavesurfer.js'
import lamejs from 'lamejs'

const wavesurfer = ref(null)
const audioPlayer = ref(null)

onMounted(() => {
  wavesurfer.value = WaveSurfer.create({
    container: '#waveform',
    waveColor: '#4F4A85',
    progressColor: '#383351',
    barWidth: 2,
    barRadius: 3,
    cursorWidth: 1,
    height: 100,
    barGap: 2
  })
})

const startRecording = () => {
  wavesurfer.value.startRecording()
}

const stopRecording = async () => {
  const blob = await wavesurfer.value.stopRecording()
  await transcodeWithLamejs(blob)
}

const transcodeWithLamejs = async (blob) => {
  // 将Blob转为ArrayBuffer
  const arrayBuffer = await blob.arrayBuffer()
  const audioContext = new (window.AudioContext || window.webkitAudioContext)()
  const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)
  
  // 提取单声道PCM数据(立体声需自行合并声道)
  const pcmData = audioBuffer.getChannelData(0)
  const samples = new Int16Array(audioBuffer.length)
  
  // 将Float32格式的音频数据转为Int16
  pcmData.forEach((sample, idx) => {
    samples[idx] = sample < 0 ? sample * 0x8000 : sample * 0x7FFF
  })
  
  // 初始化编码器:1声道、采样率、128kbps比特率
  const mp3encoder = new lamejs.Mp3Encoder(1, audioBuffer.sampleRate, 128)
  const mp3Chunks = []
  const chunkSize = 1152 // lamejs要求的固定分块大小
  
  // 分块编码MP3
  for (let i = 0; i < samples.length; i += chunkSize) {
    const chunk = samples.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))
  }
  
  // 生成MP3格式Blob并预览
  const mp3Blob = new Blob(mp3Chunks, { type: 'audio/mp3' })
  audioPlayer.value.src = URL.createObjectURL(mp3Blob)
}
</script>

<style scoped>
#waveform {
  margin: 20px 0;
}
</style>

注意事项

  • FFmpeg方案首次加载会下载WebAssembly文件,生产环境可将文件部署到CDN加速加载
  • lamejs默认仅支持单声道转码,若需立体声需自行处理声道合并逻辑
  • 生产环境使用FFmpeg方案时,服务器也需配置跨源响应头,否则SharedArrayBuffer仍会报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 18:15:30