Search code examples
expresshttpfile-uploadbackendmulter

multer error is not sent in response. request keeps pending


I am creating a file upload feature to upload multiple files(images) to the express application. I have configured the multer middleware with size limits and file mimetypes as follows :

const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, 'uploads/product_images/');
    },
    filename: function (req, file, cb) {
        cb(null, "product-" + Date.now() + '-' + uuidv4() + "-" + file.originalname);
    }
});

const upload = multer({
    storage: storage,
    limits: {
        fileSize: 1024 * 1024 * 5
    },
    fileFilter: function (req, file, cb) {
        if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
            cb(null, true);
        } else {
            cb(new Error('Invalid file type, only JPEG and PNG are allowed!'));
        }
    }
}).array("images")

to handle the errors given by the cb in multer, I have created an error handler middleware which I have place after all productRoutes as follows :

const productRouter = express.Router();

productRouter
    .get('/', fetchProductsList)
    .get('/:product_id/:size', validateProductDB, fetchProductById)
    .post('/', isAuth, upload, validateProduct, handleValidationErrors, createProduct)
    .patch('/:id', upload, updateProduct)
    .delete('/:id', deleteProduct)
    .get('/colors', fetchColors)
    .get('/sizes', fetchSizes)
    .get('/genders', fetchGenders)

// Custom error handler for Multer errors
productRouter.use((err, req, res, next) => {
    if (err instanceof multer.MulterError) {
        // A Multer error occurred when uploading.
        console.log("message from multer instance : ", err.message)
        return res.status(400).json({ errors: [{ msg: err.message }] });
    } else if (err) {
        // An unknown error occurred when uploading.
        console.log("message not from multer instance : ", err.message)
        return res.status(400).json(err.message);
    }
    next();
});

I am not getting any response when I try to upload a file which is not an image and is greater than the size limit. But I get the error printed in the console from the custom error handler middleware.

Another fact : everything works fine in postman.

What is wrong with this code. To check the full code go to this repo : https://github.com/ishanjarwal/mern-sneaker-store Please explain the reason for the problem and possible solutions.


Solution

  • The fileFilter is executed after the first chunk of a file has been received by the server. If the fileFilter then passes an error in the first argument of cb, the server stops consuming the incoming request and proceeds to the error handler, which returns the error in the response.

    If the uploaded file is small enough to be transferred to the server in one chunk, the client may receive the error response and consider the request completed. But if only the first chunk of a big file was transferred, the client may get stuck and either not receive the error response at all, or receive it but still not complete the request. This latter behavior is what I observe with curl as client:

    >curl -F [email protected] http://server/path
    {"errors":[{"msg":"Invalid file type, only JPEG and PNG are allowed!"}]}|
    

    The blinking cursor | remains after the JSON response, meaning that the request is not yet finished. But other clients (for example, Postman) may behave differently when "stuck" with such a partially consumed request.

    The default error handler of express avoids such "stuck" situations by "unpiping" everything from the request, see here. (See also busboy wait for field data before accepting file upload explains)

    Solution: If you want to exclude a file from the upload, do not throw an error but have the fileFilter execute

    req.omittedFiles = (req.omittedFiles || []).concat(file);
    cb(null, false);
    

    instead. Then that file will be omitted from req.files and you can use the contents of req.omittedFiles to inform the user about the omitted files when you construct the response.