Add support for `FormData`
gornostay25 opened this issue · comments
Version
0.1.3
Platform
Linux 4b2918398c1b 5.15.0-1012-gcp #17~20.04.1-Ubuntu SMP Thu Jun 23 16:10:34 UTC 2022 x86_64 GNU/Linux
What steps will reproduce the bug?
...
fetch: (req)=>{
console.log(req.formData) // => undefined
},
...
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior?
https://developer.mozilla.org/en-US/docs/Web/API/Request/formData
What do you see instead?
...
fetch: (req)=>{
console.log(req.formData) // => undefined
...
req.formData() //throw error request.formData is not a function. (In 'request.formData()', 'request.formData' is undefined)
...
},
...
Additional information
No response
FormData
hasn't been imported from WebKit yet. It had some dependencies on HTMLFileElement
that made it trickier. It needs to be added though
FormData
hasn't been imported from WebKit yet. It had some dependencies onHTMLFileElement
that made it trickier. It needs to be added though
Are you planning on importing it from WebKit or building a custom implementation? The API seems pretty minimal (if we would not support the optional HTMLElement
parameter in the constructor).
I found we can patch Request
to add this through a polyfill, e.g.
import multipart from 'parse-multipart-data';
import { FormData } from 'formdata-polyfill/esm.min.js';
Request.prototype.formData = async function formData() {
const boundary = multipart.getBoundary(this.headers.get('content-type'));
const buffer = Buffer.from(await new Response(this.body).text());
const parts = multipart.parse(buffer, boundary);
const form = new FormData();
for (let i = 0; i < parts.length; i++) {
form.append(parts[i].name, parts[i].data);
}
return form;
};
Interestingly enough, calling this.text()
will kill the process with a Segmentation fault: 11
-- that's why I ended up using Response
for getting the request text.
@pateketrueke Can you help me with https://github.com/gornostay25/svelte-adapter-bun? I try to add polyfill... like FormData
https://github.com/gornostay25/svelte-adapter-bun/blob/master/src/polyfills.js
@gornostay25 just add parse-multipart-data
in your dependencies, then, update your script as below:
// node_modules/@sveltejs/kit/src/exports/node/polyfills.js"
import { FormData } from "formdata-polyfill/esm.min.js";
+import multipart from "parse-multipart-data";
/** @type {Record<string, any>} */
const globals = {
FormData,
};
export default function installPolyfills() {
+ Request.prototype.formData = async function formData() {
+ const boundary = multipart.getBoundary(this.headers.get('content-type'));
+ const buffer = Buffer.from(await new Response(this.body).text());
+ const parts = multipart.parse(buffer, boundary);
+ const form = new FormData();
+
+ for (let i = 0; i < parts.length; i++) {
+ form.append(parts[i].name, parts[i].data);
+ }
+ return form;
+ };
for (const name in globals) {
Object.defineProperty(globalThis, name, {
enumerable: true,
configurable: true,
writable: true,
value: globals[name],
});
}
}
Also make sure you're including a polyfill for File
as well, I just did this:
globalThis.File = class File {
constructor(bytes, filename, options = {}) {
Object.defineProperties(this, {
name: { value: filename },
type: { value: options.type },
size: { value: bytes.length },
lastModified: { value: options.lastModified || null },
lastModifiedDate: { value: options.lastModified ? new Date(options.lastModified) : null },
arrayBuffer: { value: () => new TextEncoder().encode(bytes) },
text: { value: () => Promise.resolve(bytes.toString()) },
[Symbol.toStringTag]: { value: 'File' },
});
}
};
Implemented in #2051
Can you advice how to use it ? #621 (comment) @Jarred-Sumner
Understood now. But 2 awaits cause a prob: 500 Internal Server Error
if (pathName === "/w/Xapix") { const bodyhere2 = await req.text() // const bodyhere2 = await req.json() const formData = await req.formData(); console.log(formData.get("foo")) return new Response(null, { status: 200 }); }
I've created a middleware for uploading multipart form data.
export const saveFiles = async (c: Context, next: Next) => {
try {
const formData = await c.req.formData();
const files = formData.getAll('files');
const saveFilePromises = files.map(async (targetFile, index) => {
const file = targetFile as BunFile;
const buffer = await file.arrayBuffer();
const originalName = file.name;
const filename = `${uuidV4()}.${index}_${originalName}`;
const path = `static/${filename}`;
Bun.write(path, buffer);
return {
oroginalName: originalName,
name: filename,
path: path,
size: file.size,
type: file.type,
};
});
const result = await Promise.all(saveFilePromises);
c.set('savedFiles', result);
} catch (error: any) {
c.res = new Response(error.message, { status: 500 });
}
await next();
};