Skip to content

Commit 2eab150

Browse files
feat(api/http): properly handle Files in Body.form, closes #7390 (#7394)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio> closes #7390
1 parent fdaee9a commit 2eab150

File tree

7 files changed

+102
-78
lines changed

7 files changed

+102
-78
lines changed

.changes/api-formAsync.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tauri-apps/api': 'patch:bug'
3+
---
4+
5+
Fix `Body.form` static not reading and sending entries of type `Blob` (including subclasses such as `File`)

core/tauri/scripts/bundle.global.js

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/api/dist/assets/index.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/api/dist/assets/index.js

Lines changed: 29 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/api/src-tauri/Cargo.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tooling/api/docs/js-api.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

tooling/api/src/http.ts

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,50 @@ interface FilePart<T> {
8585

8686
type Part = string | Uint8Array | FilePart<Uint8Array>
8787

88+
type FormInput = Record<string, Part> | FormData
89+
type FormBody = Record<string, string | number[] | FilePart<number[]>>
90+
91+
async function formBody(data: FormInput): Promise<FormBody> {
92+
const form: FormBody = {}
93+
94+
const append = async (
95+
key: string,
96+
v: string | Uint8Array | FilePart<Uint8Array> | File
97+
): Promise<void> => {
98+
if (v !== null) {
99+
let r
100+
if (typeof v === 'string') {
101+
r = v
102+
} else if (v instanceof Uint8Array || Array.isArray(v)) {
103+
r = Array.from(v)
104+
} else if (v instanceof File) {
105+
r = {
106+
file: Array.from(new Uint8Array(await v.arrayBuffer())),
107+
mime: v.type,
108+
fileName: v.name
109+
}
110+
} else if (typeof v.file === 'string') {
111+
r = { file: v.file, mime: v.mime, fileName: v.fileName }
112+
} else {
113+
r = { file: Array.from(v.file), mime: v.mime, fileName: v.fileName }
114+
}
115+
form[String(key)] = r
116+
}
117+
}
118+
119+
if (data instanceof FormData) {
120+
for (const [key, value] of data) {
121+
await append(key, value)
122+
}
123+
} else {
124+
for (const [key, value] of Object.entries(data)) {
125+
await append(key, value)
126+
}
127+
}
128+
129+
return form
130+
}
131+
88132
/**
89133
* The body object to be used on POST and PUT requests.
90134
*
@@ -108,6 +152,7 @@ class Body {
108152
* but you can set it to `multipart/form-data` if the Cargo feature `http-multipart` is enabled.
109153
*
110154
* Note that a file path must be allowed in the `fs` allowlist scope.
155+
*
111156
* @example
112157
* ```typescript
113158
* import { Body } from "@tauri-apps/api/http"
@@ -131,40 +176,8 @@ class Body {
131176
*
132177
* @returns The body object ready to be used on the POST and PUT requests.
133178
*/
134-
static form(data: Record<string, Part> | FormData): Body {
135-
const form: Record<string, string | number[] | FilePart<number[]>> = {}
136-
137-
const append = (
138-
key: string,
139-
v: string | Uint8Array | FilePart<Uint8Array> | File
140-
): void => {
141-
if (v !== null) {
142-
let r
143-
if (typeof v === 'string') {
144-
r = v
145-
} else if (v instanceof Uint8Array || Array.isArray(v)) {
146-
r = Array.from(v)
147-
} else if (v instanceof File) {
148-
r = { file: v.name, mime: v.type, fileName: v.name }
149-
} else if (typeof v.file === 'string') {
150-
r = { file: v.file, mime: v.mime, fileName: v.fileName }
151-
} else {
152-
r = { file: Array.from(v.file), mime: v.mime, fileName: v.fileName }
153-
}
154-
form[String(key)] = r
155-
}
156-
}
157-
158-
if (data instanceof FormData) {
159-
for (const [key, value] of data) {
160-
append(key, value)
161-
}
162-
} else {
163-
for (const [key, value] of Object.entries(data)) {
164-
append(key, value)
165-
}
166-
}
167-
return new Body('Form', form)
179+
static form(data: FormInput): Body {
180+
return new Body('Form', data)
168181
}
169182

170183
/**
@@ -343,6 +356,11 @@ class Client {
343356
if (jsonResponse) {
344357
options.responseType = ResponseType.Text
345358
}
359+
360+
if (options.body?.type === 'Form') {
361+
options.body.payload = await formBody(options.body.payload as FormInput)
362+
}
363+
346364
return invokeTauriCommand<IResponse<T>>({
347365
__tauriModule: 'Http',
348366
message: {
@@ -546,6 +564,7 @@ export type {
546564
Duration,
547565
ClientOptions,
548566
Part,
567+
FormInput,
549568
HttpVerb,
550569
HttpOptions,
551570
RequestOptions,

0 commit comments

Comments
 (0)