Flask接收JS上传录音Librosa加载报未知音频格式错误排查
问题现象
浏览器录制6秒时长音频上传至Flask后端,计划后续将音频转换为频谱图、转为Numpy数组供模型推理时,抛出如下报错:
RuntimeError: Error opening <FileStorage: 'audio_file.wav' ('audio/wav')>: File contains data in an unknown format.
调整多版配置仍无法定位问题出在文件上传逻辑还是Librosa调用环节。
根因说明
问题共涉及4处前后端代码错误,没有单一诱因:
- 前端伪造音频格式:
MediaRecorder默认录制的音频为webm/opus编码,手动给Blob设置type: "audio/wav"仅修改了MIME标记和文件名后缀,没有做实际转码,文件本质不是WAV格式,Librosa无法识别伪装后缀的文件。 - 后端文件读取key不匹配:前端
formData.append传入的字段名是audio_file,后端写的是request.files["audio_file.wav"],字段名对应错误。 - 后端文件指针位置错误:先调用
f.save(audio)把上传文件存到本地后,FileStorage对象的指针已经移到文件末尾,直接把f传入lb.load()读不到任何有效内容。 - 路由地址不匹配:前端AJAX请求的路径是
/LungUploader,后端定义的路由是/LUploader,路径少了ng字符,正常情况下会直接返回404。 - 缺失依赖导入:代码中直接调用
specshow()但没有从librosa.display导入该方法,就算音频读取成功也会抛出名称错误。
修复方案
前端修复
不要硬编码音频MIME类型,优先调用浏览器支持的录制格式,若必须输出标准WAV格式需要引入专门的PCM转码库(如recorder.js),不能靠改MIME伪装格式,最小改动可用代码如下:
function lDUp(){ var formData = new FormData(); const startButton = document.getElementById("startLungBtn"); startButton.addEventListener("click", function() { if (!navigator.mediaDevices) { console.error("getUserMedia not supported.") return; } const constraints = { audio: true }; navigator.mediaDevices.getUserMedia(constraints) .then(function(stream) { var chunks = []; // 优先选用浏览器支持的录制格式,不硬写wav let supportMime = ''; if (MediaRecorder.isTypeSupported('audio/wav;codecs=1')) supportMime = 'audio/wav;codecs=1'; else if (MediaRecorder.isTypeSupported('audio/webm;codecs=opus')) supportMime = 'audio/webm;codecs=opus'; const recorder = new MediaRecorder(stream, {mimeType: supportMime}); recorder.ondataavailable = event => { chunks.push(event.data); }; recorder.onstop = event => { console.log("Recording stopped.") // 用录制器实际输出的MIME生成Blob let blob = new Blob(chunks, { type: recorder.mimeType }); chunks = []; startButton.disabled = false; formData.append("audio_file", blob, "audio_file"); $.ajax({ type: "POST", // 路径和后端路由对齐 url: "http://127.0.0.1:5000/LUploader", processData: false, contentType: false, cache: false, data: formData, success: function(response) { console.log(response); console.log("This is class: ", response) }, error: function(err) { console.log(err); } }); }; recorder.onstart = event => { console.log("Recording started."); startButton.disabled = true; setTimeout(function() { recorder.stop(); }, 6000); }; recorder.start(); }) .catch(function(err) { console.error(err); }); }); }
后端修复
修正字段名、路由、导入问题,直接从内存读取音频字节流,避免文件指针偏移问题,代码如下:
import librosa as lb import soundfile as sf import io from flask import request import matplotlib.pyplot as plt # 补全缺失的导入 from librosa.display import specshow # 路由和前端请求路径对齐 @app.route('/LUploader', methods=["GET", 'POST']) def lUploader(): if request.method == "POST": # 字段名和前端append的参数对齐 f = request.files["audio_file"] # 直接读内存字节流,避免指针偏移问题 audio_bytes = f.read() # 保留原生采样率不做重采样,减少失真 y, sr = lb.load(io.BytesIO(audio_bytes), sr=None) print("Checkpoint") plt.figure(figsize=(10,3)) src_ft = lb.stft(y) src_db = lb.amplitude_to_db(abs(src_ft)) specshow(src_db, sr=sr, x_axis='time', y_axis='hz') plt.ylim(0, 5000) # 补全保存逻辑,否则生成的图会丢失 plt.savefig('lung_spec.png') plt.close() # 后续可直接用y(音频numpy数组)做模型推理 return {"code": 0, "msg": "process success"}
若读取webm格式音频时报解码错误,先安装ffmpeg并加入环境变量,Librosa会自动调用ffmpeg完成非WAV格式的解码。
内容的提问来源于stack exchange,提问作者Schaedel420
相关产品推荐
相关产品推荐

