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

JS/TS客户端向Supabase Edge函数传递音频FormData失败的解决方法

问题解决与实用方案

1. Supabase客户端传FormData的坑

Supabase JS客户端的functions.invoke默认会把body序列化成JSON,还自动添加Content-Type: application/json请求头。直接传FormData的话,客户端会把FormData转成空JSON对象,服务器自然拿不到有效数据。

要搞定这个问题,得手动配置请求选项,不让客户端自动序列化body,同时别手动设置Content-Type——浏览器会自动生成带boundary的正确请求头:

修改后的客户端代码:

const formData = new FormData();

const file = await fetch(uri);
const fileBlob = await file.blob();
const filename = getFileName(uri);
formData.append("file", fileBlob, filename);

const { data, error } = await supabase.functions.invoke("openai", {
  body: formData,
  headers: {
    // 移除自动添加的Content-Type,让浏览器自行处理
    "Content-Type": undefined,
  },
  // 告知客户端不要对body做序列化处理
  bodyType: "formData",
});

2. 服务器端解析要对应

服务器端得使用正确的多部分表单解析工具,Deno环境推荐用multiparser库,示例代码如下:

import { serve } from "https://deno.land/std@0.200.0/http/server.ts";
import { multiparser } from "https://deno.land/x/multiparser@v2.1.0/mod.ts";

serve(async (req) => {
  if (!req.body) {
    console.log("没收到请求体");
    return new Response(JSON.stringify({}), { status: 400 });
  }

  try {
    const form = await multiparser(req);
    if (form.files?.file) {
      console.log("收到音频文件:", form.files.file.filename);
      return new Response(JSON.stringify({ success: true }), { status: 200 });
    }
    console.log("表单里没找到文件");
    return new Response(JSON.stringify({}), { status: 400 });
  } catch (err) {
    console.error("解析表单失败:", err);
    return new Response(JSON.stringify({ error: "解析出错" }), { status: 500 });
  }
});

3. 替代方案:用原生fetch/axios

如果Supabase客户端的配置依然有问题,直接用原生fetch发起请求会更稳妥:

const formData = new FormData();
const file = await fetch(uri);
const fileBlob = await file.blob();
const filename = getFileName(uri);
formData.append("file", fileBlob, filename);

const response = await fetch("https://<你的项目ID>.functions.supabase.co/openai", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${supabase.auth.session()?.access_token}`,
    // 同样不要手动设置Content-Type
  },
  body: formData,
});

const data = await response.json();

4. 音频传输的最优姿势

  • 首推FormData:支持携带文件名,浏览器自动处理编码逻辑,服务器端解析简单,适合大部分场景。
  • 直接传输Blob:如果不需要文件名,可以直接发送Blob,减少一层FormData的封装开销:
    客户端代码:
    const fileBlob = await (await fetch(uri)).blob();
    const { data, error } = await supabase.functions.invoke("openai", {
      body: fileBlob,
      headers: {
        "Content-Type": fileBlob.type, // 比如audio/mpeg、audio/wav等
      },
    });
    
    服务器端接收:
    serve(async (req) => {
      const blob = await req.blob();
      console.log("收到音频Blob,大小:", blob.size);
      // 这里添加音频处理逻辑
      return new Response(JSON.stringify({}));
    });
    
  • 避免使用Base64编码:Base64会让音频体积增大30%左右,除非有特殊需求,否则不推荐使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 11:49:59