如何使用TypeScript Obsidian库发送multipart/form-data有效载荷?

46scxncf  于 2023-06-24  发布在  TypeScript
关注(0)|答案(2)|浏览(152)

我正在为Obsidian.md开发一个插件,它需要向需要multipart/form-data负载中的文件的API发出HTTP请求。为了确保插件在移动的上工作,我不得不使用requestUrl方法provided by Obsidian's TypeScript lib,以确保插件在移动设备上工作,以及其他一些兼容性原因。
然而,requestUrl期望主体是stringArrayBuffer-有没有办法将FormData转换为stringArrayBuffer?还是我在构建有效载荷时采取了错误的方法?
接口:https://marcus.se.net/obsidian-plugin-docs/reference/typescript/interfaces/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);
});

当前错误:Type 'FormData' is not assignable to type 'string | ArrayBuffer | undefined'.
其他参考文献:API RepoAPI DocsA working implementation of the method

oipij1gg

oipij1gg1#

我已经建立了一个解决方案,本质上我只是检查了来自fetch/Postman的有效请求,现在正在以相同的格式手动构建multipart/form-data有效负载。
可能有一个现有的库或util函数以更清晰的方式完成此操作,如果我没有找到一个,我可以自己编写一个,如果我需要在插件的其他部分发送文件负载请求。

// This next block is a workaround to current Obsidian API limitations: requestURL only supports string data or an unnamed blob, not key-value formdata
// Essentially what we're doing here is constructing a multipart/form-data payload manually as a string and then passing it to requestURL
// I believe this to be equivilent to the following curl command: curl --location --request POST 'http://djmango-bruh:9000/asr?task=transcribe&language=en' --form 'audio_file=@"test-vault/02 Files/Recording.webm"'

// Generate the form data payload boundry string, it can be arbitrary, I'm just using a random string here
// https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data
// https://stackoverflow.com/questions/1349404/generate-random-string-characters-in-javascript
const N = 16 // The length of our random boundry string
const randomBoundryString = "djmangoBoundry" + Array(N + 1).join((Math.random().toString(36) + '00000000000000000').slice(2, 18)).slice(0, N)

// Construct the form data payload as a string
const pre_string = `------${randomBoundryString}\r\nContent-Disposition: form-data; name="audio_file"; filename="blob"\r\nContent-Type: "application/octet-stream"\r\n\r\n`;
const post_string = `\r\n------${randomBoundryString}--`

// Convert the form data payload to a blob by concatenating the pre_string, the file data, and the post_string, and then return the blob as an array buffer
const pre_string_encoded = new TextEncoder().encode(pre_string);
const data = new Blob([await this.app.vault.adapter.readBinary(fileToTranscribe.path)]);
const post_string_encoded = new TextEncoder().encode(post_string);
const concatenated = await new Blob([pre_string_encoded, await getBlobArrayBuffer(data), post_string_encoded]).arrayBuffer()

// Now that we have the form data payload as an array buffer, we can pass it to requestURL
// We also need to set the content type to multipart/form-data and pass in the boundry string
const options: RequestUrlParam = {
    method: 'POST',
    url: 'http://djmango-bruh:9000/asr?task=transcribe&language=en',
    contentType: `multipart/form-data; boundary=----${randomBoundryString}`,
    body: concatenated
};

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

全源here

emeijp43

emeijp432#

如果您不想滚动自己的文件,multi-part-lite库似乎可以很好地完成此任务:

const form = new Multipart();
form.append("session_id", sessionId);
form.append("files", Buffer.from(await this.app.vault.adapter.read("Foo.md")), { filename: "Foo.md", contentType: "text/markdown" })
const body = (await form.buffer()).toString();

console.log(body);

await request({
    url: `http://${host}/learn`,
    method: "POST",
    contentType: `multipart/form-data; boundary=${form.getBoundary()}`,
    body,
})

输出:

----MultipartBoundary283122172169
Content-Disposition: form-data; name="session_id"

6257abbf-8b7f-421a-bca5-2935150b87c1
----MultipartBoundary283122172169
Content-Disposition: form-data; name="files"; filename="Foo.md"
Content-Type: text/markdown

<Contents of Foo.md>
----MultipartBoundary283122172169--

相关问题