如何用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
相关产品推荐
相关产品推荐

