Safari环境下使用Axios录制发送音频至Laravel后端异常排查
音频录制提交至Laravel后端:Safari浏览器音频文件缺失问题排查与解决
问题概述
开发的音频录制并上传至Laravel后端功能,在Chrome、Firefox中运行正常,但在Safari浏览器提交表单后,后端接收到的音频文件为空。
相关代码
音频录制代码
const stream = await navigator.mediaDevices.getUserMedia({ audio: true }) const recorder = new MediaRecorder(stream) const chunks = [] recorder.addEventListener('dataavailable', (event) => { chunks.push(event.data) }) // 注:原代码未展示recorder.start()和stop()逻辑,这可能是关键遗漏点 const audioBlob = new Blob(chunks, { type: 'audio/webm' }) const timestamp = new Date().getTime() const filename = `audio_${timestamp}.wav` const audioFile = new File([audioBlob], filename, { type: 'audio/wav' })
表单发送代码
const formData = new FormData() formData.append('audio', audioFile, filename) console.log('formData:', formData.getAll('audio')) // 控制台输出:File {name: "audio_1686845222667.wav", lastModified: 1686845222667, webkitRelativePath: "", size: 1758, type: "audio/wav", …} const config = { headers: { //'Content-Type': 'multipart/form-data', 'Content-Type': 'audio/wav', }, } const response = await axios.post('/recordaudio', formData, config)
请求内容对比
Safari中异常的请求内容
------WebKitFormBoundaryAOTnIX35RdvABYSP Content-Disposition: form-data; name="audio"; filename="audio_1686845222667.wav" Content-Type: audio/wav ------WebKitFormBoundaryAOTnIX35RdvABYSP--
Firefox中正常的请求内容
-----------------------------285208455512739247982911496103 Content-Disposition: form-data; name="audio"; filename="audio_1686846593105.wav" Content-Type: audio/wav OggS���������wb����mSsOpusHead8»�����OggS����������wb���ü*c9OpusTags���libopus unknown������ENCODER=Mozilla110.0.1OggS��@8������wb���!áµÿÿÿ3ÿ2ÿ1ÿ0ÿ4ÿ2ÿ3ÿ4ÿ4ÿ3ÿ5ÿ3øÿþøÿþøpêÀp¯qXºàm æÐ9 pDëÀQ¨>øåYÜÙî󥳤Âo70³AqlÂÕþIëRÎ¥SÊTo_ÉVºa·þ}v2Iò5åíPDð&^Øu¼ë)âEÝ æÂ¡)R `ìÆ<׺r׿®%ìát/¼xÓ§uîh£¼nÅÕÕNÄD0QD4ò$sEVæûÖâIyU7( »~x3ôöo)'3æi);r& 6û?á_ÏâË aÕÇ9è2 7â���������������������������������������������������������������������������������������������"T õ3¿úRõ?Á!ýÏÐ{üQçüQ8µÙ%!§¼¸K�L±Y¢¿Ül¨#S°úSN§6®Þ,à\*&ñ´& ÅØ/Öè:uñRÇB;J³ ... -----------------------------285208455512739247982911496103--
已尝试方案
更换过不同文件类型及Content-Type值(multipart/form-data、audio/wav),问题未解决。
问题分析与解决方案
1. 错误的Content-Type设置
手动设置Content-Type: audio/wav会破坏FormData的自动编码逻辑,尤其是Safari对multipart/form-data的边界(boundary)处理更严格。浏览器在提交FormData时会自动生成包含boundary的multipart/form-data头部,手动设置会丢失关键的boundary信息,导致后端无法解析出文件内容。
修复:
移除手动设置的Content-Type,让axios自动处理头部:
const response = await axios.post('/recordaudio', formData)
2. MediaRecorder录制流程不完整
原代码未展示recorder.start()和recorder.stop()的调用逻辑,若未正确触发录制流程,chunks数组会为空,生成的Blob自然没有内容。此外,Safari对MediaRecorder的格式支持与Chrome/Firefox不同:
- Chrome/Firefox支持
audio/webm格式 - Safari仅支持
audio/mp4(AAC编码)格式
修复:
完善录制流程,并适配Safari的格式要求:
const stream = await navigator.mediaDevices.getUserMedia({ audio: true }) // 检测浏览器支持的MediaRecorder格式 const supportedMimeType = MediaRecorder.isTypeSupported('audio/mp4') ? 'audio/mp4' : 'audio/webm'; const recorder = new MediaRecorder(stream, { mimeType: supportedMimeType }) const chunks = [] recorder.addEventListener('dataavailable', (event) => { if (event.data.size > 0) { // 只添加非空数据块 chunks.push(event.data) } }) // 启动录制 recorder.start() // 示例:录制5秒后停止,实际需根据业务逻辑触发stop() setTimeout(() => { recorder.stop() }, 5000) // 等待录制结束后生成文件并提交 recorder.addEventListener('stop', async () => { if (chunks.length === 0) { console.error('录制内容为空') return } const audioBlob = new Blob(chunks, { type: supportedMimeType }) const timestamp = new Date().getTime() // 根据格式设置正确的文件名后缀 const ext = supportedMimeType === 'audio/mp4' ? 'm4a' : 'webm' const filename = `audio_${timestamp}.${ext}` const audioFile = new File([audioBlob], filename, { type: supportedMimeType }) const formData = new FormData() formData.append('audio', audioFile, filename) const response = await axios.post('/recordaudio', formData) })
3. 验证Blob有效性
Safari中直接将Blob转为File时,若Blob为空或格式不兼容,可能生成无效文件。需确保生成Blob后先验证其大小:
const audioBlob = new Blob(chunks, { type: supportedMimeType }) if (audioBlob.size === 0) { console.error('生成的音频Blob为空') return }
4. Laravel后端接收适配
确保Laravel后端的路由和控制器正确处理multipart/form-data请求,示例控制器代码:
public function store(Request $request) { if (!$request->hasFile('audio')) { return response()->json(['error' => '未接收到音频文件'], 400); } $audioFile = $request->file('audio'); // 验证文件类型 $validated = $request->validate([ 'audio' => 'required|file|mimes:webm,m4a,wav', ]); // 保存文件逻辑 $path = $audioFile->store('audio', 'public'); return response()->json(['path' => $path]); }
内容的提问来源于stack exchange,提问作者01011001
相关产品推荐
相关产品推荐

