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

如何用Obsidian TypeScript库发送multipart/form-data请求体?

问题

为Obsidian.md开发插件时,需要向接收multipart/form-data格式的API发送HTTP请求,出于移动端兼容性要求必须使用Obsidian提供的requestUrl方法,但该方法仅接受string或ArrayBuffer类型的请求体,直接传入FormData会报类型错误:

Type 'FormData' is not assignable to type 'string | ArrayBuffer | undefined'

Obsidian的RequestUrlParam接口定义如下:

/** @public */
export interface RequestUrlParam {
    /** @public */
    url: string;
    /** @public */
    method?: string;
    /** @public */
    contentType?: string;
    /** @public */
    body?: string | ArrayBuffer;
    /** @public */
    headers?: Record<string, string>;
    /**
     * Whether to throw an error when the status code is >= 400
     * Defaults to true
     * @public
     */
    throw?: boolean;
}

原错误代码:

const formData = new FormData();
const data = new Blob([await this.app.vault.adapter.readBinary(fileToTranscribe.path)]);
formData.append("audio_file", data);

const options: RequestUrlParam = {
  method: "POST",
  url: "http://djmango-bruh:9000/asr?task=transcribe&language=en",
  contentType: "multipart/form-data",
  body: formData,
};

requestUrl(options).then((response) => {
  console.log(response);
}).
catch((error) => {
  console.error(error);
});
解决方案

由于requestUrl不直接支持FormData,需要手动构建符合multipart/form-data规范的请求体,并转换为ArrayBuffer,具体实现如下:

// 生成随机边界符,用于分隔表单字段
const boundary = `----ObsidianFormBoundary${Math.random().toString(36).substring(2, 15)}`;
// 读取文件二进制数据
const fileBuffer = await this.app.vault.adapter.readBinary(fileToTranscribe.path);

// 构建multipart内容的各个部分
const parts = [
  // 字段头部
  `--${boundary}\r\n`,
  `Content-Disposition: form-data; name="audio_file"; filename="${fileToTranscribe.name}"\r\n`,
  `Content-Type: audio/mpeg\r\n`, // 根据实际文件类型修改
  `\r\n`,
  // 文件二进制数据
  new Uint8Array(fileBuffer),
  // 结束边界
  `\r\n--${boundary}--\r\n`
];

// 将所有部分拼接成一个Uint8Array
const uint8Parts: Uint8Array[] = [];
for (const part of parts) {
  if (typeof part === 'string') {
    // 将字符串转换为Uint8Array
    const encoder = new TextEncoder();
    uint8Parts.push(encoder.encode(part));
  } else {
    uint8Parts.push(part);
  }
}

// 计算总长度并合并所有Uint8Array
const totalLength = uint8Parts.reduce((acc, part) => acc + part.length, 0);
const combinedBuffer = new Uint8Array(totalLength);
let offset = 0;
for (const part of uint8Parts) {
  combinedBuffer.set(part, offset);
  offset += part.length;
}

// 转换为ArrayBuffer
const requestBody = combinedBuffer.buffer;

// 构建requestUrl参数
const options: RequestUrlParam = {
  method: "POST",
  url: "http://djmango-bruh:9000/asr?task=transcribe&language=en",
  contentType: `multipart/form-data; boundary=${boundary}`, // 必须包含boundary
  body: requestBody,
};

requestUrl(options).then((response) => {
  console.log(response);
}).catch((error) => {
  console.error(error);
});

关键说明

  • 边界符(boundary):必须在contentType中声明,且与请求体中的分隔符一致,确保服务端能正确解析表单数据
  • 文件类型:Content-Type需根据实际音频文件类型修改(如audio/wav、audio/mp3等)
  • 二进制拼接:需要将字符串头部和文件二进制数据统一转换为Uint8Array后合并,最终转为ArrayBuffer符合requestUrl的要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 02:20:54