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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 16:45:03