Search code examples
javascripthtmlnode.jsformsexpress

Send a post request from HTML form to NodeJS app


I have a problem with this form i made, that doesn't send anything to the following node js app. I tried using a tool like postman to send the request and it's working (files sent are saved on the folder I want them to be saved), but if i submit files from the form it doesn't work. I'm very new to web developing, nodeJS and express, I'm still learning. I would like the files uploaded to index.html to be saved on a folder called "uploads". This is the folder organization I'm using

HTML

<!DOCTYPE html>

<html lang="ita">
  <head>
    <!-- style imprort -->
    <link rel="stylesheet" href="style.css" />
    <link rel="icon" type="image/x-icon" href="assets/imgs/temp-icon.png" />

    <!-- script import -->
    <script
      type="text/javascript"
      src="https://code.jquery.com/jquery-latest.js"
    ></script>
    <script src="index-script.js"></script>

    <title>temp-title</title>
  </head>

  <body>
    <form
      method="POST"
      action="/upload"
      name="form"
      class="form vert-align"
      enctype="multipart/form-data"
    >
      <h1 class="first-page">FILE DA MANDARE AD AZURE FORM RECOGNIZER</h1>

      <div class="container1">
        <!-- custom upload button -->
        <label for="file" class="label-style-1 upload-label">UPLOAD FILE</label>
        <input type="file" id="file" name="file" multiple hidden />
        <!-- aggiungere per accettare solo determinati tipi di file -> accept=".estensione1, .estensione2" -->

        <span>No file uploaded</span>
      </div>

      <div class="container3">
        <!-- custom submit button -->
        <label for="submit" class="label-style-1 submit-label" hidden>OK</label>
        <input type="submit" id="submit" name="submit" hidden />
        <!-- da impostare come tpye="submit" successivamente -->
        <!-- custom reset button -->
        <label for="cancel" class="label-style-1 cancel-label" hidden
          >CANCEL</label
        >
        <input type="reset" id="cancel" name="cancel" hidden />
      </div>
    </form>
  </body>
</html>

----------------------------------------

app NodeJS

/* modules necessary */
const express = require("express");
const path = require("path");
const multer = require("multer");

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "uploads");
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + "-" + path.basename(file.originalname));
  },
});

const upload = multer({ storage: storage });

/* new instance of express object */
const app = express();

app.use(express.static(path.join(__dirname, "public")));

app.post("/upload", upload.array("file"), (req, res) => {
  res.send("Uploaded");
});

/* handles all requests without a corresponding route */
app.use((req, res) => {
  res.status(404);
  res.send("<h1>Error 404: Resource not found</h1>");
});

/* where the app is hosted */
app.listen(3000);

----------------------------------------

script js

/* regular expression to show thousand separator */
function numberWithDots(x) {
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}
let myFiles;
let formData = new FormData();

$(document).ready(function () {
  $("#cancel").on("click", function () {
    location.reload();
  });

  $("#submit").on("click", function () {
    window.open("results.html", "_self");
  });

  $("#file").on("change", (event) => {
    myFiles = event.target.files;

    for (let i = 0; i < myFiles.length; i++) {
      let file = myFiles[i];
      formData.append("files", file, file.name);
    }

    /* creates a container for the list that will be created */
    $(".container1").html(
      "<div class='container2'>" +
        "<span class='pop-head'>Uploaded:</span> <ul></ul></div>"
    );

    $(".first-page").css("margin-top", "10vh");

    /* writes the name and size of the files chosen */
    for (let i = 0; i < event.target.files.length; i++) {
      $("ul").append(
        "<li><div class='container4'><span href='" +
          event.target.files[i] +
          "' download>" +
          event.target.files[i].name +
          " (" +
          numberWithDots(Math.floor(event.target.files[i].size / 1024 + 1)) +
          " KB)</span>    " +
          "<label for='remove" +
          i +
          "' class='label-style-1 remove' id='lab" +
          i +
          "'>REMOVE</label>" +
          "<input type='button' id='remove" +
          i +
          "' name='remove" +
          i +
          "' hidden>" +
          "</div></li>"
      );
    }

    /* removes upload file button */
    $(".upload-label").remove();
    $("#file").remove();
    /* shows OK & CANCEL buttons */
    $(".submit-label").show();
    $(".cancel-label").show();

    ///* TO IMPLEMENT SERVER SIDE *///
    $(".remove").on("click", function () {
      let index = $(this).attr("id").replace("lab", "");
      let fileToRemove = this.files[index];
      // Esegui l'azione di rimozione del file qui
      console.log("File da rimuovere:", fileToRemove);
      // Rimuovi l'elemento <li> corrispondente dall'elenco
      $(this).closest("li").remove();
    });
  });
});

----------------------------------------

css

@font-face {
  font-family: WorkSans;
  src: url("assets/fonts/WorkSans.ttf");
}
* {
  font-family: "WorkSans";
}

/*---------Chromium---------*/
/* width */
::-webkit-scrollbar {
  width: 10px;
}

/* Track */
::-webkit-scrollbar-track {
  background: #595959;
}

/* Handle */
::-webkit-scrollbar-thumb {
  background: #f1ca13;
}

/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
  background: #c19f0b;
}

/*---------Mozilla---------*/
html {
  scrollbar-width: 10px;
  scrollbar-color: #f1ca13 #595959;
}

body {
  background-color: #383838;
  font-weight: 300;
  font-size: 20pt;
  margin: 0px;
  overflow-y: scroll;
}

.form {
  background-color: #383838;
  color: white;

  width: 70vw;
  margin: auto;

  text-align: center;
}

.vert-align {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.sticky {
  position: fixed;
  top: 0;
  width: 100%;
  text-align: center;
}
.content {
  padding-top: 50px;
}

h1 {
  font-size: 60pt;
  color: #f1ca13;
  background-color: #383838;
  margin-bottom: 50px;
}
.first-page {
  margin-top: 40vh;
}

span {
  color: white;
}
ul {
  list-style-type: none;
}
li {
  padding: 15px;
  margin: 15px;
  border: 1px solid #696969;
  cursor: default;
}

.label-style-1 {
  padding: 3px 20px;
  border-radius: 30px;
  border: 3px solid black;
  background-color: black;
  transition: all 200ms linear;
  font-size: 20px;
}
.label-style-1:hover {
  border: 3px solid white;
  background-color: white;
  color: black;
  cursor: pointer;
}

.upload-label {
  width: 150px;
}

.submit-label {
  width: 30px;
}

.pop-head {
  font-size: 25pt;
  color: #f1ca13;
  font-weight: bold;
}

.container2 {
  width: 60vw;
  padding: 40px;

  text-align: left;

  border: 3px solid white;
  border-radius: 10px;
}
.container2 ul {
  display: flex;
  flex-direction: column;
}

.container2 li:hover {
  background-color: #595959;
  cursor: default;
}

.container3 {
  padding: 30px;
}

.container4 {
  display: flex;
  align-items: center;
}

.container5 {
  padding: 15px;
  margin: 15px;
  border: 1px solid #696969;
  cursor: default;
}

.container6 {
  text-align: center;
  width: 44rem;
}

.container7 {
  cursor: pointer;
  width: fit-content;
}

.remove {
  margin-left: auto; /* aligns to the right */
}

/* -------------------------results------------------------- */
.results {
  overflow-y: scroll;
  color: white;
  display: flex;
  align-items: center;
  min-height: 100vh;
  flex-direction: column;
}

.file {
  width: fit-content;
  display: inline-block;
}

.details {
  display: inline-block;
  position: relative;
  margin-left: 10px;
  font-size: 10pt;
  color: #f1ca13;
  transition: all 200ms linear;
}

.details:hover {
  color: #b5960f;
}

.details::after {
  content: "";
  position: absolute;
  width: 100%;
  transform: scaleX(0);
  height: 1px;
  bottom: -1px;
  left: 0;
  background-color: #f1ca13;
  transition: transform 100ms linear;
}
.details:hover::after {
  transform: scaleX(1);
  transform-origin: center;
  background-color: #b5960f;
}

.file-box {
  display: none;
  padding: 0 18px;
  position: relative;
  grid-template-columns: 1fr 1fr;
  gap: 1em;
}

.input-box {
  position: relative;
  margin-top: 1em;
}

.input-box label {
  position: absolute;
  top: -22px; /* 18px; */
  left: 28px; /* 18px */
  padding: 2px 10px;
  border-left: 1px solid #696969;
  border-right: 1px solid #696969;
  border-top: 1px solid #383838;
  border-bottom: 1px solid #383838;
  border-radius: 6px;
  color: white;
  background-color: #383838;
  transition: all 400ms ease;
}

.input-box input {
  padding: 20px;
  border: 1px solid #696969;
  background: #383838;
  border-radius: 6px;
  outline: none;
  color: white;
  font-size: 0.8em;
}

.input-box input:focus {
  animation: animated-border 1500ms linear infinite;
}

@keyframes animated-border {
  0%,
  100% {
    border: 1px solid #696969;
  }
  50% {
    border: 1px solid #f1ca13;
  }
}

I tried a lot of things but being that I’m new I don't know exactly what to search for.


Solution

  • In you script file on submit button click, it was redirecting to new html page, which have replace with fetch api call method to access the nodeJS endpoint to pass formdata, once api return success after file upload you can redirect to corresponding page .

    /* regular expression to show thousand separator */
    function numberWithDots(x) {
      return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
    }
    let myFiles;
    let formData = new FormData();
    
    $(document).ready(function () {
      $("#cancel").on("click", function () {
        location.reload();
      });
    
      $("#submit").on("click", function (e) {
        
        e.preventDefault();
        
        fetch("http://localhost:3000/upload", {
          method: 'POST',
          body: formData
        })
          .then((res) => console.log(res))
          .catch((err) => ("Error occured", err));
    
    
      });
    
      $("#file").on("change", (event) => {
        myFiles = event.target.files;
    
        for (let i = 0; i < myFiles.length; i++) {
          let file = myFiles[i];
          formData.append("files", file, file.name);
        }
    
        /* creates a container for the list that will be created */
        $(".container1").html(
          "<div class='container2'>" +
          "<span class='pop-head'>Uploaded:</span> <ul></ul></div>"
        );
    
        $(".first-page").css("margin-top", "10vh");
    
        /* writes the name and size of the files chosen */
        for (let i = 0; i < event.target.files.length; i++) {
          $("ul").append(
            "<li><div class='container4'><span href='" +
            event.target.files[i] +
            "' download>" +
            event.target.files[i].name +
            " (" +
            numberWithDots(Math.floor(event.target.files[i].size / 1024 + 1)) +
            " KB)</span>    " +
            "<label for='remove" +
            i +
            "' class='label-style-1 remove' id='lab" +
            i +
            "'>REMOVE</label>" +
            "<input type='button' id='remove" +
            i +
            "' name='remove" +
            i +
            "' hidden>" +
            "</div></li>"
          );
        }
    
        /* removes upload file button */
        $(".upload-label").remove();
        $("#file").remove();
        /* shows OK & CANCEL buttons */
        $(".submit-label").show();
        $(".cancel-label").show();
    
        ///* TO IMPLEMENT SERVER SIDE *///
        $(".remove").on("click", function () {
          let index = $(this).attr("id").replace("lab", "");
          let fileToRemove = this.files[index];
          let z = [];
          for (let i = 0; i < myFiles.length; i++) {
          const file = myFiles[i];
          if (file.name !== fileToRemove.name) {
            z.push(file)
          }
         }
          myFiles = z;
    
          // Esegui l'azione di rimozione del file qui
          console.log("File da rimuovere:", fileToRemove);
          // Rimuovi l'elemento <li> corrispondente dall'elenco
          $(this).closest("li").remove();
        });
      });
    });
    

    Also in index.html removed the form-action attribute

    <form
      method="POST"
      name="form"
      class="form vert-align"
      enctype="multipart/form-data"
    >
    

    In App.js

    Multer upload field name was different upload.array("file") instead of upload.array("files") , which will lead to exception in api and return 500 status response in api.

    So the app.js code in total is :

    /* modules necessary */
    const express = require("express");
    const path = require("path");
    const multer = require("multer");
    
    const storage = multer.diskStorage({
    
      destination: (req, file, cb) => {
        cb(null, "uploads");
      },
      filename: (req, file, cb) => {
        cb(null, Date.now() + "-" + path.basename(file.originalname));
      },
    });
    
    const upload = multer({ storage: storage });
    
    /* new instance of express object */
    const app = express();
    
    app.use(express.static(path.join(__dirname, "public")));
    
    app.get("/",(req,res)=>{
        res.sendFile(__dirname+'/public/assets/index.html')
    })
    
    app.post("/upload", upload.array("files"), (req, res) => {
    
      console.log(req);
    
      res.send("Uploaded");
    });
    
    /* handles all requests without a corresponding route */
    app.use((req, res) => {
      res.status(404);
      res.send("<h1>Error 404: Resource not found</h1>");
    });
    
    /* where the app is hosted */
    app.listen(3000);
    

    You can read more on this article https://blog.logrocket.com/multer-nodejs-express-upload-file/