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

<Request>.prototype.formData() Image Upload Bug #3969

Closed
porterporter opened this issue Aug 4, 2023 · 5 comments
Closed

<Request>.prototype.formData() Image Upload Bug #3969

porterporter opened this issue Aug 4, 2023 · 5 comments
Labels
bug Something isn't working web-api Something that relates to a standard Web API

Comments

@porterporter
Copy link

What version of Bun is running?

0.7.2

What platform is your computer?

Darwin 22.5.0 arm64 arm (MacOS)

What steps can reproduce the bug?

When running .formData() on req, it should create a formData object with the body of request. Instead, the program halts and no further code is ran.

Discord Thread: https://discord.com/channels/876711213126520882/876711213126520885/1136905295172489226

Repro
import { BunFile } from "bun";
import { resolve } from "path";

const UPLOADS = resolve('data/uploads');

Bun.serve({
    port: 3000,
    async fetch(req) {
      const url = new URL(req.url);
      if (url.pathname !== "/upload") {
        return new Response(null, { status: 404 });
      }
      if (req.method !== "POST") {
        return new Response(null, { status: 405 });
      }
      if (!req.body) {
        return new Response(null, { status: 400 });
      }

      const buff = await req.arrayBuffer()
      console.log(buff)
      // output: ArrayBuffer(6941268) [ 45, 45, 97, 108, 97, 109, 111...


      // nothing happens after here ⬇️
      const formData = await req.formData();

      // not logged
      console.log(req, formData)

      const file = (formData.get('file') ?? formData.get('image') ?? formData.get('video')) as BunFile;
      if(!file) return new Response(null, { status: 422 });

      await Bun.write(resolve(UPLOADS, Date.now().toString()), file)

      return new Response(null, { status: 201 });
    }
  });
  console.log("Listening On Port 3000")
My test uploaded image

appIconImage

What is the expected behavior?

No response

What do you see instead?

No response

Additional information

FormData.from(await req.arrayBuffer(), req.headers.get("content-type").split("boundary=").at(-1)) was a working alternative solution.

@porterporter porterporter added the bug Something isn't working label Aug 4, 2023
@robobun robobun added the web-api Something that relates to a standard Web API label Aug 4, 2023
@porterporter
Copy link
Author

More Repro
const server = Bun.serve({
	async fetch(req) {
		console.log(req.method)
		const url = new URL(req.url);
		if (url.pathname !== "/upload") {
			return new Response(null, { status: 404 });
		}
		if (req.method !== "POST") {
			return new Response(null, { status: 405 });
		}
		if (!req.body) {
			return new Response(null, { status: 400 });
		}

		const formData = await req.formData();

		// const formData = await toFormData(req);

		const blob = (formData.get('file') ?? formData.get('image') ?? formData.get('video')) as Blob;
		console.log(blob)

		return new Response(null, { status: 201 });
	},
	error(error) {
		console.log(error);
		return new Response(null, { status: 500 });
	}
});
console.log(`[SERVER] Listening ${server.hostname}:${server.port}`)

export function isNullOrUndefined(value: unknown): value is null | undefined {
	return value === undefined || value === null;
}

export async function toFormData(request: Request): Promise<FormData> {
	// @ts-expect-error - BUN: FormData.from is undocumented.
	return FormData.from(await request.arrayBuffer(), request.headers.get("content-type")?.slice("boundary=").at(-1));
}
Curl Request
curl -v http://localhost:3000/upload -X POST -F file=@IMG_7109.jpg

Note: IMG_7109.jpg is a 2.7 MB JPEG

Bun Error
(error: Internal error: task for FormData must not be null)
Work Around

By using

FormData.from(await request.arrayBuffer(), request.headers.get("content-type")?.slice("boundary=").at(-1));

instead of req.formData(), blob contains a 2.7 MB Blob with type image/jpeg, however the filename is not present as expected.

Note: FormData.from() is an undocumented method.

@Hanaasagi
Copy link
Collaborator

Hanaasagi commented Aug 5, 2023

Although not clear why, but it seems that commenting out the following lines makes it work.

    // if (!req.body) {
    //   return new Response(null, { status: 400 });
    // }
Code
const server = Bun.serve({
  async fetch(req) {
    console.log(req.method);
    const url = new URL(req.url);
    if (url.pathname !== "/upload") {
      return new Response(null, { status: 404 });
    }
    if (req.method !== "POST") {
      return new Response(null, { status: 405 });
    }
    // if (!req.body) {
    //   return new Response(null, { status: 400 });
    // }

    const formData = await req.formData();

    // const formData = await toFormData(req);

    const blob = (formData.get("file") ??
      formData.get("image") ??
      formData.get("video")) as Blob;
    console.log(blob);

    return new Response(null, { status: 201 });
  },
  error(error) {
    console.log(error);
    return new Response(null, { status: 500 });
  },
});
console.log(`[SERVER] Listening ${server.hostname}:${server.port}`);


It looks like if req.body was accessed earlier, this code snippet sets value.action.getFormData to null.

defer {
form_data.?.deinit();
value.action.getFormData = null;
}

An error is thrown here.

var async_form_data = locked.action.getFormData orelse {
promise.reject(global, ZigString.init("Internal error: task for FormData must not be null").toErrorInstance(global));
break :inner;

@porterporter
Copy link
Author

Yup, that fixed it. Is there any reason that when uploading an image the fileName property wouldn't be available though?

@Jarred-Sumner
Copy link
Collaborator

It would appear as .name on the Blob

@porterporter
Copy link
Author

image

logging file.name, prints undefined

Jarred-Sumner added a commit that referenced this issue Aug 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working web-api Something that relates to a standard Web API
Projects
None yet
Development

No branches or pull requests

4 participants