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

Expo录制音频转MP3/M4A及上传FileStack问题排查

问题解决:Expo录制M4A音频并上传至FileStack

一、解决录制M4A格式不生效的问题

你遇到的扩展名仍为.caf的问题,是因为prepareToRecordAsync传入的RECORDING_OPTIONS_PRESET_HIGH_QUALITY预设覆盖了构造器里的自定义配置。正确做法是将格式配置合并到预设选项中,而非分开设置。

修改后的录制代码:

import * as React from "react";
import { Text, View, StyleSheet, Button } from "react-native";
import { Audio } from "expo-av";

export default function App() {
  const [recording, setRecording] = React.useState();
  const [recordedUri, setRecordedUri] = React.useState(); // 保存录制后的URI用于上传

  async function startRecording() {
    try {
      console.log("请求权限..");
      await Audio.requestPermissionsAsync();
      await Audio.setAudioModeAsync({
        allowsRecordingIOS: true,
        playsInSilentModeIOS: true,
        shouldDuckAndroid: true,
      });
      console.log("开始录制..");
      // 合并预设与自定义格式配置
      const recordingOptions = {
        ...Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY,
        extension: ".m4a",
        // iOS指定AAC编码器,确保生成M4A
        audioEncoder: Audio.RECORDING_OPTION_IOS_AUDIO_ENCODER_AAC,
        // Android配置对应编码器和输出格式
        android: {
          audioEncoder: Audio.RECORDING_OPTION_ANDROID_AUDIO_ENCODER_AAC,
          outputFormat: Audio.RECORDING_OPTION_ANDROID_OUTPUT_FORMAT_MPEG_4,
        },
      };
      const recording = new Audio.Recording();
      await recording.prepareToRecordAsync(recordingOptions);
      await recording.startAsync();
      setRecording(recording);
      console.log("录制已开始");
    } catch (err) {
      console.error("录制启动失败", err);
    }
  }

  async function stopRecording() {
    console.log("停止录制..");
    setRecording(undefined);
    await recording.stopAndUnloadAsync();
    const uri = recording.getURI();
    setRecordedUri(uri); // 保存URI供上传使用
    console.log("录制已停止,文件路径:", uri);
  }

  return (
    <View style={styles.container}>
      <Button
        title={recording ? "停止录制" : "开始录制"}
        onPress={recording ? stopRecording : startRecording}
      />
      {recordedUri && <Text style={{marginTop:16}}>录制文件路径:{recordedUri}</Text>}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    backgroundColor: "#ecf0f1",
    padding: 10,
  },
});

关键修改点:

  • 将自定义格式配置合并到预设选项中,避免被覆盖
  • 明确指定iOS/Android的音频编码器为AAC,Android输出格式设为MPEG_4,确保生成标准M4A文件
  • 添加recordedUri状态存储文件路径,方便后续上传

二、上传至FileStack的正确实现

直接传递文件URI无法完成上传,Filestack要求接收multipart/form-data格式的文件内容,需用FormData包装文件后发送请求。

修改后的上传代码(整合到App组件内):

import axios from "axios"; // 需先安装:npm install axios

// 在App组件内添加上传函数
async function uploadFileAndGetURL() {
  console.log("开始上传...");
  console.log("本地文件路径:", recordedUri);
  if (!recordedUri) return;

  try {
    const formData = new FormData();
    const fileName = recordedUri.split("/").pop();
    formData.append("file", {
      uri: recordedUri,
      name: fileName,
      type: "audio/m4a",
    });

    const response = await axios.post(
      `https://www.filestackapi.com/api/store/S3?key=${你的FILESTACKAPI_KEY}`,
      formData,
      {
        headers: {
          "Content-Type": "multipart/form-data",
        },
      }
    );

    if (response.status === 200) {
      console.log("上传完成!");
      console.log("文件URL:", response.data.url);
      return response.data.url;
    }
  } catch (error) {
    console.error("上传出错:", error);
    throw error;
  }
}

在组件return中添加上传按钮:

{recordedUri && (
  <Button 
    title="上传至FileStack" 
    onPress={uploadFileAndGetURL} 
    style={{marginTop: 20}} 
  />
)}

关键注意事项:

  • 必须用FormData包装文件,不能直接传递URI
  • 请求头需设置为multipart/form-data,而非固定的audio/m4a
  • 替换你的FILESTACKAPI_KEY为实际的Filestack API密钥
  • 通过split("/").pop()获取文件名,适配iOS和Android的路径格式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 15:57:50