Search code examples
node.jsexpressmulter

Can't Use Multer Properly In A Controller (Node/Express.js)


I'm having an issue with file uploading with Multer and unfortunately, I don't have a lot of experience with Multer. In this project, I'm trying to structure things such that routes call functions (controllers) which run commands, for example creating a product etc.

I'm pretty sure I set up Multer correctly, however when I try to req.file.filename in the controller it returns an undefined.

This is my setup (currently Multer is a helper function, I'm going to move it to middleware after as this is incorrect).

File Storage Helper Func

const multer = require("multer");

//SET Storage
let storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "public/uploads");
  },
  filename: function (req, file, cb) {
    const fileName = file.originalname.replace(" ", "-");
    cb(null, fileName + "-" + Date.now());
  },
});
const uploadOptions = multer({ storage });

module.exports = { uploadOptions };

Product Router

const express = require("express");
const router = express.Router();

//@Helpers
const { uploadOptions } = require("./../../Helpers/FileStorage.helper");

//@Categories
const {
  getProducts,
  postProduct
} = require("./../../Controllers/Products/product.controller");

//GET ALL Products + ADD NEW Product
router
  .get("/", getProducts)
  .post("/", uploadOptions.single("image"), postProduct);

And finally, the post product controller:

//POST A New Product
const postProduct = expressAsyncHandler(async (req, res) => {
  const category = await Categories.findOne(req.body.category);
  if (!category) return res.status(400).send("Category is invalid, try again!");


  const fileName = req.file.filename;
  const basePath = `${req.protocol}://${req.get("host")}/public/upload/`;

  const product = new Product({
    name: req.body.name,
    description: req.body.description,
    richDescription: req.body.richDescription,
    image: `${basePath}${fileName}`,
    brand: req.body.brand,
    price: req.body.price,
    category,
    countInStock: req.body.countInStock,
    rating: req.body.rating,
    numReviews: req.body.numReviews,
    isFeatured: req.body.isFeatured,
  });
  const productList = await Product.findOne({ name: product.name });
  if (productList != null) {
    return res.status(404).send("Product already exists! Please try again!");
  }
  try {
    await product.save();
    res.status(200).send(product);
  } catch (e) {
    res.status(500).send("Product was not created! Error: " + e.message);
  }
});

I know the traditional way of doing this in routes would be the following (which works!):

router.post("/", uploadOptions.single("image"), async(req,res) => {
//Run function
}

However, as I mentioned above, I'm trying to break the route actions up into controller functions. When console.log(req.file) it returns an undefined.

I suspect the props aren't being passed to the postProduct function which is what causes the error, but I can't figure out how to resolve this. I've been staring at this too long, perhaps it's an easy thing to resolve and I'm being stupid (highly probable).

If someone could assist me in fixing this and explain where I'm going wrong, I would be eternally grateful.

Edit: This is the ERROR: " TypeError: Cannot read property 'filename' of undefined "


Solution

  • I dont think that you are parsing the form data correctly:

    // this is for parsing json data
    app.use(express.json());
    // this is for parsing form data
    app.use(express.urlencoded({ extended: false }));