0

I have the Node/Express server with multer for file upload. Form contains 3 fields and they are Title, Description and the Image.

From postman in the body I select form-data and then passing the title, description and then the image.

My route

app.post("/uploads", (req, res, next) => {
  upload.single("photo")(req, res, (err) => {
    const { title, description } = req.body;
    if (!title || !description) {
      return res
        .status(400)
        .json({ error: "Title and description are required" });
    }

    if (err) {
      return res.status(400).json({ error: err.message });
    }
    if (!req.file) {
      console.log(req.file);
      return res.status(400).json({ error: "File not uploaded." });
    }

    res.send(req.file);
  });
});

Scenario 1 - When proper inputs (title, description and valid image) are given then image is saved.

Scenario 2 - When valid image but no presence of title or description in the request then simply return the err response saying title/description is needed. But here the err response is sent and at the same time image is also uploaded and I need to prevent it.

Only when all 3 inputs are valid then save the image otherwise no need to save it.

Multer seems to save the image first before the title/description validation happens.

I also tried with another approach by adding a middleware to validate the title/description but its also not working. Because I get undefined as its the form data.

const validateFields = (req, res, next) => {
  const { title, description } = req.body;
  console.log(title + " " + description); //its undefined
  if (!title || !description) {
    return res.status(400).send("title and description are neededdd");
  }
  next();
};
app.post( "/uploads", validateFields, upload.single("photo"), async (req, res) => {
    const { title, description } = req.body;
    console.log(req.file);
    res.send("uploaded");
  }
);

Multer config

import multer from "multer";
import { v4 as uuidv4 } from "uuid";
import path from "path";

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

const fileFilter = (req, file, cb) => {
  const allowedFileTypes = ["image/jpeg", "image/jpg", "image/png"];
  if (allowedFileTypes.includes(file.mimetype)) {
    cb(null, true); // Accept the file
  } else {
    cb(
      new Error("Invalid file type, only JPEG, JPG, and PNG is allowed!"),
      false
    );
  }
};

const upload = multer({
  storage,
  fileFilter,
  limits: { fileSize: 10 * 1024 * 1024 },
}); //1MB limit

export default upload;

How to handle this - is this possible or do I need to use some other alternative way to fix this.

3
  • Does this help? The way I used to design my system is to validate and save all the form fields first and then only after success, upload the file to server, using multer. May be overhead, but something you can try Commented Jun 19 at 7:49
  • 1
    Just came across this npm package Pechkin , need to check on it Commented Jun 19 at 8:25
  • 1
    @gopinathkrm, I have posted my detailed answer.Please check and let me know your feedback.
    – Subha
    Commented Jun 19 at 9:19

3 Answers 3

1

Scenario 2 - When valid image but no presence of title or description in the request then simply return the err response saying title/description is needed. But here the err response is sent and at the same time image is also uploaded and I need to prevent it.

You need to delete the uploaded file in case validation fails.So try this code.

app.post("/uploads", (req, res, next) => {
  
  upload.single("photo")(req, res, (err) => {
    
    const { title, description } = req.body;
    
    if (!title || !description || !req.file) {
      // If any of the fields are missing, delete the uploaded file (if any)
      if (req.file) {
        // Delete the uploaded file if it exists
        fs.unlinkSync(req.file.path);  // make sure to provide correct file path
      }
      return res.status(400).json({ error: "Title, description, and file are required" });
    }

    if (err) {
      
      return res.status(400).json({ error: err.message });
    }

    // If all fields are validated, send the uploaded file object
    res.send(req.file);
  });
});

N:B :- Remember to import Node core-module i.e const fs = require('fs'); to the top of the script.

Try this code. I hope it will resolve the issue, if not then let me know.

4
  • this works fine, thanks - I am curious to know, when multer is used we have only this option to handle this scenario right ? Or do we any other way ? Commented Jun 19 at 9:37
  • Instead of manual validation You should use validation library like express-validator or schema based validation library i.e. joi.It depends on your preference. You will get to know more about these packages from npm registry.Thanks for upvoting.
    – Subha
    Commented Jun 19 at 10:35
  • Okay got it and finally one more doubt, using these packages affects the text field validation alone right ? not related to image upload. I meant if i use express-validator or joi I still need to use the unlink to remove the uploaded file if any error occurs or it will be handled in a different way ? Commented Jun 19 at 10:46
  • Even if there are different ways to handle this I am sorry to say I am not aware of it. But in my projects I have been using this approach. But my wild guess says there must be some ways to deal with this that I need to look for. This is indeed a very good question. But currently I don't have answer of it.
    – Subha
    Commented Jun 19 at 11:30
1

Multer can do validations. It allows validations during the process of upload. The fileFilter property in Multer can be used for validations too.

The example code below shows the same, performing a validation - a userId authentication, before the upload starts. It then proceeds with the upload if the validation succeeds else aborts the upload.

As you know, in your case, fileFilter is already in use. Therefore please add the validations for title and description as well.

server.js

const express = require('express');
const multer = require('multer');

const app = express();

const upload = multer({
  dest: './uploads',
  fileFilter: async function (req, file, cb) {
    const checkUserId = async (userid) => {
      if (userid > 0 && userid < 10) {
        cb(null, true);
      } else {
        cb(new Error(`${userid} is an invalid userId - valid ids [1-9]`));
      }
    };
    await checkUserId(req.body.userid);
  },
});

Citation :

Trouble Accessing req.body in Middleware: Getting Empty Response

7
  • I did but inside the file filter when try to access the title, description, I get undefined. const fileFilter = async (req, file, cb) => { const { title, description } = req.body; const allowedFileTypes = ["image/jpeg", "image/jpg", "image/png"]; const validateInputs = async () => { if (!title || !description) { return cb(new Error(Title required)); } else { cb(null, true); } }; await validateInputs(); if (allowedFileTypes.includes(file.mimetype)) { cb(null, true); / } else { cb(new Error("Invalid file type!"), false); } }; Commented Jun 20 at 4:42
  • Please refer it like this : req.body.title , it will work since req is a parameter to this filter function. Commented Jun 20 at 5:00
  • Okay its my mistake, I can access the title & description, but again facing the same issue, when the field inputs are invalid and image is a valid one, then the image is uploaded. Error response saying title is needed is sent and image is also uploaded. How to prevent this ? Even i added the input validation very top in the file filter function. My goal is to perform validation and if ok then only upload the image. Commented Jun 20 at 5:26
  • Can you please update your question with this latest code. Commented Jun 20 at 5:31
  • code and question is same - perform validation for inputs first (checking for title and description) and then upload the file. If there is any err then return the err response. The current issue is when I am not passing the title or description in the request I get the correct err response but the image is also uploaded . The only change is I added a if block for title and description inside the file filter as you mentioned. Shall I share the git repo if needed? Commented Jun 20 at 5:44
1

To perform the field input validation first and then upload the file - We can modify the file filter function since it has access to the request.

Route Logic

app.post("/uploads", (req, res, next) => {
  upload.single("photo")(req, res, (err) => {
    // access input fields err
    if (err) {
      return res.status(400).json({ error: err.message });
    }
    // access file upload err
    if (err instanceof multer.MulterError) {
      return res.status(400).json({ error: err.message });
    }
    // no file is passed
    if (!req.file) {
      return res.status(400).json({ error: "File not uploaded." });
    }
    res.send("file uploaded!");
  });
});

Multer config File Filter

const allowedFileTypes = ["image/jpeg", "image/jpg", "image/png"];

const fileFilter = async (req, file, cb) => {
  const { title, description } = req.body;

  console.log(title, description);
  if (!title || !description) {
    return cb(new Error("Title or description is missing"), false);
  }
  if (!allowedFileTypes.includes(file.mimetype)) {
    const error = new multer.MulterError(
      "LIMIT_UNEXPECTED_FILE",
      file.fieldname
    );
    error.message = "Invalid file type, only JPEG, JPG, and PNG is allowed!";
    console.log(error);
    return cb(error, false);
  }
  return cb(null, true); // Accept the file
};

Now when the title or description is not passed in the request, err message will be passed to the route and we can access the error there and send response based on that. For any err related to file upload then create an instance of multer err

1
  • Yeah this is the better one.Upvoted
    – Subha
    Commented Jun 20 at 15:06

Not the answer you're looking for? Browse other questions tagged or ask your own question.