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

React Native Expo调用Flask API遇Network Request Failed问题求助

问题:React Native Expo调用Flask语音转写API报"Network Request Failed"

React Native Expo应用通过本地IP调用Node.js API时,模拟器和实体Android设备均正常,但调用同局域网内的Flask语音转写API时,出现"Network Request Failed"错误。Flask API部署在本地机器(地址:http://192.168.x.x:5000),实体Android设备与电脑连接同一WiFi网络。

Flask API代码

from flask import Flask, request, jsonify
import base64
import tempfile
import speech_recognition as sr
from pydub import AudioSegment
from io import BytesIO
from flask_cors import CORS
import logging

app = Flask(__name__)
CORS(app, resources={
    r"/*": {
        "origins": "*",
        "methods": ["GET", "POST", "OPTIONS"],
        "allow_headers": ["Content-Type", "uthorization"]
    }
})

logging.basicConfig(level=logging.DEBUG)

def transcribe_audio(audio_base64):

    # Decode the base64 audio data
    try:
        audio_data = base64.b64decode(audio_base64)
    except Exception as e:
        return f"Error decoding base64 audio: {str(e)}"
    
    # Convert base64 audio to a format that SpeechRecognition can use (WAV)
    audio_file = BytesIO(audio_data)
    audio = AudioSegment.from_file(audio_file)

    # Create a temporary file to store the WAV audio
    with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_file:
        audio.export(temp_file.name, format="wav")
        temp_file_path = temp_file.name

    # Use SpeechRecognition to transcribe the audio
    recognizer = sr.Recognizer()
    try:
        with sr.AudioFile(temp_file_path) as source:
            audio_listened = recognizer.record(source)
            transcription = recognizer.recognize_google(audio_listened)
            return transcription
    except Exception as e:
        return f"Error transcribing audio: {str(e)}"

@app.route('/health',methods=['GET'])
def health():
    logging.info("Health check endpoint called")
    return "Hello world from python"

@app.route('/transcribe', methods=['POST'])
def transcribe():
    # Get the base64 audio data from the POST request
    print("here")
    data = request.json
    print(data)
    if not data or 'audio_base64' not in data:
        return jsonify({"error": "Missing audio_base64 in request"}), 400

    audio_base64 = data['audio_base64']

    # Transcribe the audio
    transcription = transcribe_audio(audio_base64)
    
    if "Error" in transcription:
        return jsonify({"error": transcription}), 500

    return jsonify({"transcription": transcription}), 200

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True,threaded=True)

React Native Expo代码

import { Audio } from "expo-av";
import { MutableRefObject } from "react";
import * as Filesystem from "expo-file-system";
import { Platform } from "react-native";
import * as Device from "expo-device";
import axios from "axios"

export const transcribeSpeechAssembly = async (
  audioRecordingRef: MutableRefObject<Audio.Recording>
) => {
  const isPrepared = audioRecordingRef?.current?._canRecord;
  if (!isPrepared) {
    console.error("Recording must be prepared first");
    return undefined;
  }

  try {
    await audioRecordingRef?.current?.stopAndUnloadAsync();
    const recordingUri = audioRecordingRef?.current?.getURI() || "";
    const baseUri = await Filesystem.readAsStringAsync(recordingUri, {
      encoding: Filesystem.EncodingType.Base64
    });

    const rootOrigin =
      Platform.OS === "android"
        ? "My local IP"
        : Device.isDevice
        ? process.env.LOCAL_DEV_IP || "localhost"
        : "localhost";

    const serverUrl = `http://${rootOrigin}:5000`;

    if (recordingUri && baseUri) {
      console.log("url",`${serverUrl}/transcribe`)
      const api = axios.create({
        baseURL: serverUrl,
        timeout: 10000,
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json'
        }
      });

      try {
        const healthCheck = await api.get('/health');
        console.log("Health check response:", healthCheck.data);

        const transcriptionResponse = await api.post('/transcribe', {
          audio_base64: baseUri
        });

        console.log("Transcription response:", transcriptionResponse.data);
        return transcriptionResponse.data?.transcription;
      } catch (error) {
        console.error("error from python server",error)
      }
    } else {
      console.error("Something went wrong with recording");
      return undefined;
    }
  } catch (error) {
    console.error("Error in transcription process:", error);
    return undefined;
  }
};

已尝试的排查步骤

  • 确认Flask API正常运行:Postman和浏览器访问http://127.0.0.1:5000/health和http://192.168.x.x:5000/health均返回"Hello world from python";
  • 检查Expo网络请求:Node.js API(http://192.168.x.x:3000)能正常调用,但Flask的/transcribe接口返回"Network Request Failed";
  • 配置Flask接受外部连接:使用app.run(host='0.0.0.0', port=5000, debug=True, threaded=True);
  • 处理CORS问题:通过flask_cors允许所有来源;
  • 验证Android权限:AndroidManifest.xml已添加<uses-permission android:name="android.permission.INTERNET" />;
  • 尝试adb反向代理:实体设备无效果;
  • 关闭防火墙/杀毒软件:无改善;
  • Chrome调试器查看:fetch请求失败,提示"Network Request Failed"。

排查与解决方案

1. 调整请求超时与Flask请求大小限制

语音转写的base64数据通常体积较大,当前10秒超时可能不足,且Flask默认1MB的请求大小限制容易被突破:

  • React Native端延长axios超时时间:
    const api = axios.create({
      baseURL: serverUrl,
      timeout: 30000, // 改为30秒
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      }
    });
    
  • Flask端设置更大的请求容量:
    app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 允许16MB请求
    

2. 清理base64数据前缀

Expo录制的音频转base64后可能带有data:audio/xxx;base64,前缀,导致Flask解码失败,需先移除:

let baseUri = await Filesystem.readAsStringAsync(recordingUri, {
  encoding: Filesystem.EncodingType.Base64
});
// 移除data URI前缀
baseUri = baseUri.replace(/^data:audio\/[^;]+;base64,/, '');

3. 增强Flask日志捕获

修改Flask的/transcribe接口,用logging替代print,便于排查请求是否到达及具体错误:

@app.route('/transcribe', methods=['POST'])
def transcribe():
    logging.debug("Transcribe endpoint triggered")
    try:
        data = request.json
        logging.debug(f"Received request keys: {data.keys() if data else 'No JSON data'}")
        if not data or 'audio_base64' not in data:
            return jsonify({"error": "Missing audio_base64 in request"}), 400
        
        audio_base64 = data['audio_base64']
        transcription = transcribe_audio(audio_base64)
        
        if "Error" in transcription:
            return jsonify({"error": transcription}), 500
        return jsonify({"transcription": transcription}), 200
    except Exception as e:
        logging.error(f"Server error in transcribe: {str(e)}")
        return jsonify({"error": f"Server error: {str(e)}"}), 500

4. 测试简化请求

先用一段短音频的base64字符串测试接口,排除大文件问题:

// 替换为短测试音频的base64
const testAudioBase64 = "UklGRkoQAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA";
const transcriptionResponse = await api.post('/transcribe', {
  audio_base64: testAudioBase64
});

5. 配置Android明文HTTP访问

Android 9及以上默认禁止明文HTTP请求,需在Expo的app.json中开启:

{
  "expo": {
    "android": {
      "usesCleartextTraffic": true,
      // 其他Android配置
    }
  }
}

内容的提问来源于stack exchange,提问作者Nisarg jadhav

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 07:39:56