Search code examples
javascriptnode.jsexpressmulter

Express.js - Multer File Filter Not Working


I want to filter the files while uploading them to the server but the Multer fileFilter method is not working.

Backend

destination and filename methods are working as well, but fileFilter is not working.

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "./public/images/");
  },
  filename: function (req, file, cb) {
    cb(null, file.fieldname + path.extname(file.originalname));
  },
  fileFilter: function (req, file, callback) {
    console.log("Hello Word"); // This doesn't work either

    var ext = path.extname(file.originalname);
    if (!['.jpg', '.jpeg'].includes(ext)) {
      return callback(new Error('Only images are allowed'));
    }

    callback(null, true);
  },
});

app.use(
  multer({ storage: storage }).fields([
    { name: "logo", maxCount: 1 },
    { name: "favicon", maxCount: 1 },
  ])
);

Frontend

<form action="" method="post" enctype="multipart/form-data">
    <table>
        <tbody>
            <tr>
                <td>Logo</td>
                <td>
                    <input type="file" name="logo" accept="image/jpeg, image/jpg"/>
                </td>
            </tr>
            <tr>
                <td>Favicon</td>
                <td>
                    <input type="file" name="favicon" accept="image/x-icon" />
                </td>
            </tr>
        </tbody>
    </table>
</form>

Solution

  • Your filterMethod works fine. This can be checked by changing the value in input accept="image/*" Then when we try to load a file other than ico or jpg we will get an error : Only images are allowed. Your code needs some tweaking, additionaly i used the ejs module, here is my suggestion for a solution:


    Folder & File project structure:

    enter image description here

    app.js - With Your fileFilter code.

    const path = require("path");
    const express = require("express");
    const multer = require("multer");
    const app = express();
    
    app.use(express.static(path.join(__dirname, "public")));
    app.set("view engine", "ejs");
    app.set("views", path.join(__dirname, "/views"));
    
    const storage = multer.diskStorage({
      destination: function (req, file, cb) {
        cb(null, "./images");
      },
      filename: function (req, file, cb) {
        console.log(req.file);
    
        cb(null, new Date().toUTCString() + " - " + file.originalname);
      },
    });
    
    const upload = multer({
      storage: storage,
      fileFilter: function (req, file, callback) {
        console.log("Hello, World! Works fine;-)");
    
        var ext = path.extname(file.originalname);
        if (![".jpg", ".ico"].includes(ext)) {
          return callback(new Error("Only images are allowed"));
        }
    
        callback(null, true);
      },
    });
    
    app.post(
      "/post",
      upload.fields([
        { name: "logo", maxCount: 1 },
        { name: "favicon", maxCount: 1 },
      ]),
      (req, res, next) => {
        const files = req.files;
        if (!files) {
          const error = new Error("Please upload a file");
          error.httpStatusCode = 400;
          return next(error);
        }
        res.send(files);
        console.log("Success", req.files);
      }
    );
    
    app.get("/post", (req, res) => {
      res.render("post");
    });
    
    app.listen(3000, () => {
      console.log(`Example app listening at http://localhost:3000/post`);
    })
    

    post.ejs - With Your frontend form code and my additional input submit:

    <form action="" method="post" enctype="multipart/form-data">
      <table>
        <tbody>
          <tr>
            <td>Logo</td>
            <td>
              <input type="file" name="logo" accept="image/jpeg, image/jpg" />
            </td>
          </tr>
          <tr>
            <td>Favicon</td>
            <td>
              <input type="file" name="favicon" accept="image/x-icon" />
            </td>
          </tr>
        </tbody>
      </table>
      <input class="submit" type="submit" value="Upload File" />
    </form>
    

    http://localhost:3000/post route output in browser:

    enter image description here


    Output after submit - Upload File. Files specified in fields name logo and favicon.

    enter image description here


    Output on server side and in images folder:

    enter image description here

    Tested with: node v16.13.0 "ejs": "^3.1.6","express": "^4.17.2","multer": "^1.4.4"