如何在JavaScript中不使用POST方法上传录制的音频Blob到服务器
Flask语音录制应用:音频上传与处理时序问题解决
问题描述
基于Flask开发语音录制应用,需实现:
- 点击「停止录制」时,仅将音频保存到服务器磁盘
- 点击「提交」按钮后,再根据用户选择的目标语言处理音频
当前实现中,停止录制时通过XMLHttpRequest的POST请求将音频Blob上传到根路由,直接触发了后端的处理逻辑——但此时用户尚未选择目标语言,导致程序异常。需要调整方案,避免提前触发处理流程。
相关代码
停止录制与上传音频的JavaScript代码
function stopRecording() { console.log("stopButton clicked"); //disable the stop button, enable the record too allow for new recordings stopButton.disabled = true; recordButton.disabled = false; pauseButton.disabled = true; //reset button just in case the recording is stopped while paused pauseButton.innerHTML="Pause"; //tell the recorder to stop the recording rec.stop(); //stop microphone access gumStream.getAudioTracks()[0].stop(); rec.exportWAV(saveAudioFile); } function saveAudioFile(blob) { // the file name is taken from python code.. for some reason this one is not considered var filename = new Date().toISOString(); var xhr=new XMLHttpRequest(); xhr.onload=function(e) { if(this.readyState === 4) { console.log("Server returned: ",e.target.responseText); } }; var fd=new FormData(); fd.append("audio_data",blob, filename); xhr.open("POST","/",true); xhr.send(fd); }
HTML代码
<h2>Step 1: Record your voice</h2> <br> <div align="center" id="controls"> <button type="button" class="btn btn-danger" id="recordButton">Start Recording</button> <button type="button" class="btn btn-secondary" id="pauseButton">Pause</button> <button type="button" class="btn btn-primary" id="stopButton">Stop Recording</button> </div> <div align="center" id="formats">Format: start recording to see sample rate</div> <br> <h2>Step 2: Select your target language</h2> <br> <form class="languageSelect" align="center" method="POST"> <select class="form-select" name="languageSelector" id="languageSelector" aria-label="Select your target language"> <option selected>Select target language</option> <option value="ar">Arabic</option> <option value="zh-Hans">Chinese Simplified</option> <option value="de">German (Germany)</option> <option value="ja">Japanese</option> <option value="fr">French</option> <option value="it">Italian</option> </select> <br> <br> <button class="btn btn-success" id="submit2" onclick="loading()" type="submit"> <i class="fa fa-circle-o-notch fa-spin" style="display:none;"></i> <span class="btn-text">Submit</span> </button> </form> <br> <!-- inserting these scripts at the end to be able to use all the elements in the DOM --> <script src="/static/js/recorder.js"></script> <script src="/static/js/app.js"></script> <!-- below script will show the loading animation on button click..--> <script type="text/javascript"> function loading() { $(" .btn .fa-circle-o-notch").show(); $(" .btn .btn-text").html("Please Wait..."); } </script>
Flask后端代码
@app.route('/', methods=['GET', 'POST']) def home(): if request.method == "POST": lang = request.form.getlist("languageSelector") print("Language Selected: " + str(lang)) filename = 'audio.wav' filepath = os.path.join('audio-files', filename) print(request.files) f = request.files['audio_data'] with open(filepath, 'wb') as audio: f.save(audio) print('file uploaded successfully') translted_text = translate_audio_to_text(filepath, lang) synthesize_audio(translted_text) return render_template('home.html', request="POST") else: return render_template('home.html')
解决方案
方案1:新增专门的音频上传路由
通过拆分路由,让音频上传和业务处理分离:停止录制时上传到专属路由只保存文件,提交时再读取文件处理。
修改后的JavaScript(saveAudioFile函数)
function saveAudioFile(blob) { var filename = new Date().toISOString(); var xhr=new XMLHttpRequest(); xhr.onload=function(e) { if(this.readyState === 4) { console.log("Server returned: ",e.target.responseText); } }; var fd=new FormData(); fd.append("audio_data",blob, filename); // 指向专门的音频上传路由 xhr.open("POST","/upload-audio",true); xhr.send(fd); }
修改后的Flask后端代码
import os from flask import Flask, request, render_template, session app = Flask(__name__) AUDIO_DIR = 'audio-files' os.makedirs(AUDIO_DIR, exist_ok=True) # 会话密钥,生产环境需替换为安全值 app.secret_key = 'your-secure-secret-key' @app.route('/', methods=['GET', 'POST']) def home(): if request.method == "POST": lang = request.form.get("languageSelector") if not lang or lang == "Select target language": return render_template('home.html', error="请选择目标语言") audio_filename = session.get('audio_filename') if not audio_filename: return render_template('home.html', error="请先录制音频") filepath = os.path.join(AUDIO_DIR, audio_filename) if not os.path.exists(filepath): return render_template('home.html', error="音频文件已失效,请重新录制") print(f"Language Selected: {lang}") print('开始处理音频...') translted_text = translate_audio_to_text(filepath, lang) synthesize_audio(translted_text) return render_template('home.html', request="POST") else: return render_template('home.html') @app.route('/upload-audio', methods=['POST']) def upload_audio(): if 'audio_data' not in request.files: return "未检测到音频文件", 400 f = request.files['audio_data'] filename = f.filename or f"audio_{int(os.times()[4])}.wav" filepath = os.path.join(AUDIO_DIR, filename) with open(filepath, 'wb') as audio: f.save(audio) # 将文件名存入会话,供后续处理使用 session['audio_filename'] = filename print(f'文件 {filename} 上传成功') return "音频上传成功", 200
方案2:前端暂存Blob,提交时一并上传
停止录制时将音频Blob存在前端,点击提交按钮时,把音频和语言选择一起提交到根路由,一次请求完成上传与处理。
修改后的JavaScript代码
// 全局变量暂存音频Blob let recordedAudioBlob = null; function stopRecording() { console.log("stopButton clicked"); stopButton.disabled = true; recordButton.disabled = false; pauseButton.disabled = true; pauseButton.innerHTML="Pause"; rec.stop(); gumStream.getAudioTracks()[0].stop(); // 将Blob暂存到全局变量,不立即上传 rec.exportWAV(function(blob) { recordedAudioBlob = blob; console.log("音频已暂存至前端"); }); } // 监听表单提交事件,手动构建请求 document.querySelector('.languageSelect').addEventListener('submit', function(e) { e.preventDefault(); // 阻止默认表单提交 // 校验音频和语言选择 if (!recordedAudioBlob) { alert("请先录制音频"); return; } const selectedLang = document.getElementById('languageSelector').value; if (!selectedLang || selectedLang === "Select target language") { alert("请选择目标语言"); return; } // 显示加载动画 loading(); // 构建包含音频和语言的FormData const fd = new FormData(); fd.append("audio_data", recordedAudioBlob, new Date().toISOString()); fd.append("languageSelector", selectedLang); // 发送请求 const xhr = new XMLHttpRequest(); xhr.onload = function(e) { if (this.readyState === 4) { console.log("Server returned: ", e.target.responseText); // 处理完成后刷新页面 window.location.reload(); } }; xhr.open("POST", "/", true); xhr.send(fd); });
修改后的Flask后端代码
@app.route('/', methods=['GET', 'POST']) def home(): if request.method == "POST": lang = request.form.get("languageSelector") if not lang or lang == "Select target language": return render_template('home.html', error="请选择目标语言") if 'audio_data' not in request.files: return render_template('home.html', error="请录制音频") filename = 'audio.wav' filepath = os.path.join('audio-files', filename) f = request.files['audio_data'] with open(filepath, 'wb') as audio: f.save(audio) print('file uploaded successfully') translted_text = translate_audio_to_text(filepath, lang) synthesize_audio(translted_text) return render_template('home.html', request="POST") else: return render_template('home.html')
内容的提问来源于stack exchange,提问作者StuckInPhDNoMore
相关产品推荐
相关产品推荐

