Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for FormData #621

Closed
gornostay25 opened this issue Jul 12, 2022 · 10 comments
Closed

Add support for FormData #621

gornostay25 opened this issue Jul 12, 2022 · 10 comments
Labels
enhancement New feature or request web-api Something that relates to a standard Web API

Comments

@gornostay25
Copy link
Contributor

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

@gornostay25 gornostay25 added bug Something isn't working needs repro Needs an example to reproduce labels Jul 12, 2022
@Jarred-Sumner
Copy link
Collaborator

FormData hasn't been imported from WebKit yet. It had some dependencies on HTMLFileElement that made it trickier. It needs to be added though

@Jarred-Sumner Jarred-Sumner changed the title request.formData is not a function. (In 'request.formData()', 'request.formData' is undefined) Add support for FormData Jul 12, 2022
@Jarred-Sumner Jarred-Sumner added enhancement New feature or request and removed bug Something isn't working needs repro Needs an example to reproduce labels Jul 12, 2022
@sno2
Copy link
Collaborator

sno2 commented Jul 12, 2022

FormData hasn't been imported from WebKit yet. It had some dependencies on HTMLFileElement 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).

@pateketrueke
Copy link

pateketrueke commented Jan 17, 2023

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.

@gornostay25
Copy link
Contributor Author

@pateketrueke
Copy link

@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' },
    });
  }
};

gornostay25 pushed a commit to gornostay25/svelte-adapter-bun that referenced this issue Jan 19, 2023
 - [`Request.prototype.formData`](https://www.npmjs.com/package/parse-multipart-data)
 - [`File`](oven-sh/bun#621 (comment))
Update packages
Some fixes
@Jarred-Sumner
Copy link
Collaborator

Implemented in #2051

@terrywh
Copy link

terrywh commented Mar 2, 2023

Serialize FormData but missing content-type "boundary"? @Jarred-Sumner

image

image

@bitdom8
Copy link

bitdom8 commented Jul 6, 2023

Can you advice how to use it ? #621 (comment) @Jarred-Sumner

@bitdom8
Copy link

bitdom8 commented Jul 6, 2023

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 }); }

@seungmincho
Copy link

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();
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request web-api Something that relates to a standard Web API
Projects
None yet
Development

No branches or pull requests

8 participants