Search code examples
javascriptnode.jsmultipartform-dataform-submit

How i can handle multipart form data without using any node modules


I am trying to submit a image file using a POST Request, to server, where in the body of the front end fetch request i added body as FormData like this

let formdata = new FormData(form)
async function finalFetch(formdata){
  let postReq = await fetch('/api/fileupload', {method : 'POST', body : formdata})
  let result = await postReq.json()
  return result.url;
}

Now on submit of this form in the backend i am handling the data like this

  req.on('data', (chunk)=>{
       console.log(chunk);
  })
  req.on('end', ()=>{
        // pseudo code, this will get replaced by something else
       res.write(JSON.stringify({
           msg : 'File Upload done',
           url : '/232'
  }));
      res.end();
 })

and the above implementation gives me a Buffer, i have no idea how to write file in the server form this Buffer

I know there are lots of modules out there to handle the forms like multer, express, formidable, but i don't want to use any of them i am trying to understand how these packages work. I am just using core node js.


Solution

  • There are tons of third party packages why not use them, one of the famous one is https://www.npmjs.com/package/multer, by default node dosnt give you an option to write files.

    Update - In response to a lot of comments, that want to know how to re-invent the wheel one more time, and waste their time.

    Since I was getting a lot of downvotes, so I thought I explain some things -

    The structure of multipart form data is defined by HTTP standard, the node can read the HTTP message, and the entire HTTP parser is there, it will parse the message and write it down.

    So let's create a simple utility that will do the same thing on top of the existing node utility (built-in modules) - you can integrate it with the express project as a middleware - but using createServer is the same thing, express is just a layer on top of the default HTTP module.

    Let's understand what you have to do -

    • A Request is an instance of IncomingRequest, it will do some header parsing for you, that's where express gets its headers
    • IncomingRequest extends Stream.Readable so its an stream, and you can listen for event on("data") and process that data in `on("end") event when data has been received.

    Parsing it correctly is the big issue, not everyone knows every bit of syntax or not everyone like's reading RFC of HTTP to get how multipart/formdata is sent, that's what multer does for us, it passes the data ( or rather i should say body of HTTP message ) and writes that into a file, and its good at it

    I have tried to explain how to get data, I don't know how to write parser, so you can doit yourself, if you have time

    const express = require("express");
    const app = express();
    
    
    function parseMultipartFormData(contentType, data){
        // ! Todo - You can do it yourself, you can see what data is read the RFC, and make a parser
    
        // ! That the reason people use multer, or other body handler and others
    }
    
    // middleware function for handing multipart formdata
    function multipartDataHandler(req, res, next) {
    
        // ! Warning : dont use it in production
    
        // this header will let you check the content boundry
        let contentType = req.headers['content-type'];
    
        // this is the body of HTTP message, which you send via your browser
        let data = '';
    
    
        req.on('data', (chunk)=>{
            data += chunk;
        });
        req.on("end", ()=>{
            // if you console.log data here, you will zibberish body of http. that is what we have to parse.
            parseMultipartFormData(contentType, data)
        })
    
        next();
    }
    
    
    
    
    // will send a sample page to browser with a form that will be submitted with a file.
    app.get("/", function (req, res, next) {
        res.set({
            'Content-Type': 'text/html',
        }).status(200).send(`<!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>File Upload Form</title>
        </head>
        <body>
            <h2>File Upload Form</h2>
            <form action="/" method="post" enctype="multipart/form-data">
                <label for="file">Choose a file:</label>
                <input type="file" name="file" id="file" accept=".png">
                <br>
                <input type="submit" value="Upload">
            </form>
        </body>
        </html>`);
    });
    
    // this is where things get handled, post specific things for form submission
    app.post("/", multipartDataHandler, function (req, res, next) {
        // using middleware you will set the file objects into the request like req.files
        //  and save them here
    
        // ! someday when I have more time, and skills ( I dont know how to write a propr parser I might actually do it here)
    })
    
    app.listen(8000, () => {
        console.log('Listening at PORT 8000');
    })
    

    It's okay to ask questions I guess, even though the stack overview community generally will bombard such questions — but don't be stupid by avoiding the pre-existing, industrial-scale tested utilities like multer, use them and don't waste your time reinventing the wheel - understand principles, take a course on computer networking you will not have to ask such questions anymore. Tomorrow you will ask how to send main using node, without using a module - nobody will reply such questions.