react-mic音频转换方案咨询:将weba转为MP3/MP4/WAV
Hey there! Let's tackle this audio format conversion problem you're facing. I've dealt with similar React + Rails audio workflows before, so here are practical frontend and backend solutions to get you past this hurdle:
Frontend JS Library Options (No Backend Dependencies)
Since you're looking to handle conversion on the frontend, these libraries will let you transform the WebM blob from react-mic directly into your desired format before uploading:
1. ffmpeg.wasm (Most Versatile)
This is a browser port of FFmpeg, supporting nearly all audio/video format conversions. It's powerful and straightforward once set up:
Step 1: Install Dependencies
npm install @ffmpeg/ffmpeg @ffmpeg/core
Step 2: Modify Your React Component's onStop and onUpload Methods
Update your onStop handler to convert the WebM blob to MP3 (or WAV/MP4) before uploading:
import React from 'react'; import { ReactMic } from 'react-mic'; import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'; const ffmpeg = createFFmpeg({ log: true }); class AudioMic extends React.Component { constructor(props){ super(props) this.state = { recordAudio: false, blobAudio: null, blobURL: null }; }; startRecording = () => { this.setState({ recordAudio: true }); } stopRecording = () => { this.setState({ recordAudio: false }); } onStart = () => { console.log('You can tap into the onStart callback'); } onStop = async (blobAudio) => { // Load FFmpeg core first (only needs to happen once, but safe to call multiple times) if (!ffmpeg.isLoaded()) { await ffmpeg.load(); } const inputFilename = 'recording.webm'; const outputFilename = 'recording.mp3'; // Write the WebM blob to FFmpeg's virtual file system ffmpeg.FS('writeFile', inputFilename, await fetchFile(blobAudio.blob)); // Run conversion command (adjust output format by changing the extension: .wav, .mp4) await ffmpeg.run('-i', inputFilename, outputFilename); // Read the converted file back const convertedData = ffmpeg.FS('readFile', outputFilename); const convertedBlob = new Blob([convertedData.buffer], { type: 'audio/mp3' }); // Update state with converted blob this.setState({ blobAudio: { blob: convertedBlob, blobURL: URL.createObjectURL(convertedBlob) } }, () => this.onUpload()); }; onUpload = () => { const formData = new FormData(); // Append the converted blob directly (no need for FileReader) formData.append('audio', this.state.blobAudio.blob, 'recording.mp3'); fetch('/api/v1/user_response', { credentials: 'same-origin', method: 'POST', body: formData, headers: { 'Accept': 'application/json, */*' // Don't set Content-Type here — the browser auto-handles multipart/form-data boundaries } }) .then(response => { if (!response.ok) throw new Error(`${response.status} (${response.statusText})`); return response.json(); }) .then(body => console.log('Upload successful:', body)) .catch(error => console.error('Upload error:', error.message)); }; componentDidMount = () => { const hasGetUserMedia = !!(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia); if(!hasGetUserMedia) { alert('Your browser cannot stream from your audio. Please switch to Chrome or Firefox.'); } }; render() { return ( <div> <ReactMic className='oscilloscope' record={ this.state.recordAudio } backgroundColor='#FF4081' visualSetting='sinewave' audioBitsPerSecond= { 128000 } onStop={ this.onStop } onStart={ this.onStart } strokeColor='#000000' /> <div> <audio ref='audioSource' controls='controls' src={ this.state.blobURL }></audio> </div> <button onClick={ this.startRecording }> <span>Start Recording</span> <span hidden>🎤</span> </button> <button onClick={ this.stopRecording }> <span>Stop Recording</span> <span hidden>⏹️</span> </button> <button onClick={ this.onUpload }> <span>Upload Response</span> <span hidden>☁️</span> </button> </div> ); }; };
2. lamejs (Lightweight for WAV → MP3)
If you first convert WebM to WAV (using ffmpeg.wasm or another tool), lamejs is a tiny library dedicated to WAV-to-MP3 conversion:
import lamejs from 'lamejs'; // Assume you have a WAV ArrayBuffer from conversion const convertWavToMp3 = (wavArrayBuffer) => { const wav = new Uint8Array(wavArrayBuffer); const wavHeader = lamejs.WavHeader.readHeader(new DataView(wav.buffer)); const samples = new Int16Array(wav.buffer, wavHeader.dataOffset, wavHeader.dataLen / 2); const mp3Encoder = new lamejs.Mp3Encoder(1, wavHeader.sampleRate, 128); // 1 channel, 44.1kHz, 128kbps const mp3Data = []; const chunkSize = 1152; 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) mp3Data.push(new Uint8Array(mp3Buf)); } const finalBuf = mp3Encoder.flush(); if (finalBuf.length > 0) mp3Data.push(new Uint8Array(finalBuf)); return new Blob(mp3Data, { type: 'audio/mp3' }); };
Backend Solution (Alternative to sox)
If you prefer handling conversion on the Rails side, swap carrierwave-audio with streamio-ffmpeg — it's easier to set up and supports more formats:
Step 1: Add the Gem
# Gemfile gem 'streamio-ffmpeg'
Run bundle install, then install FFmpeg on your server:
- Ubuntu/Debian:
sudo apt install ffmpeg - macOS:
brew install ffmpeg
Step 2: Update Your CarrierWave Uploader
class UserResponseUploader < CarrierWave::Uploader::Base include CarrierWave::FFmpeg storage :file if Rails.env.test? storage :fog unless Rails.env.test? # Auto-convert uploaded files to MP3 (change to .wav or .mp4 as needed) process convert: 'mp3' def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end # Optional: Customize conversion settings def convert(format) manipulate! do |media| media.encoder = 'libmp3lame' media.audio_bitrate = 128 media end end end
Final Recommendations
- Frontend conversion (ffmpeg.wasm) is great for reducing backend load and giving users immediate feedback. It's also easier to debug since you can test the conversion directly in the browser.
- Backend conversion (streamio-ffmpeg) is better if you need to enforce consistent format standards or handle large files that might strain client devices.
内容的提问来源于stack exchange,提问作者nmadoug

