Search code examples
javascriptnode.jsmongodbexpressmulter

File size limits in file upload using multer


I'm implementing a file uploader that accepts various file types, including images and videos. I need to manage the maximum file sizes differently for images and videos: images should have a maximum size of 10MB, while videos should be limited to 100MB. I'm looking to accomplish this functionality using a single instance of Multer, a middleware for handling multipart/form-data.

import path from "path";
import { TEMP_DIRECTORY_PATH } from "../constants.js";
import { ApiError } from "../utils/ApiError.js";

const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, TEMP_DIRECTORY_PATH);
    },
    filename: function (req, file, cb) {
        const fileName = Date.now() + "-" + Math.round(Math.random() * 1e9);
        const fileExtension = path.extname(file.originalname);
        cb(null, fileName + fileExtension);
    },
});

const fileFilter = (req, file, cb) => {
    const imageMimeTypes = [
        "image/png",
        "image/jpeg",
        "image/webp",
        "image/svg+xml",
    ];
    const videoMimeTypes = ["video/mp4", "video/webm"];

    const allowedMimeTypesForFields = {
        avatar: imageMimeTypes,
        coverImage: imageMimeTypes,
        videoFile: videoMimeTypes,
        thumbnail: imageMimeTypes,
        featuredImage: imageMimeTypes,
    };

    const allowedMimeTypesForField = allowedMimeTypesForFields[file.fieldname];

    if (!allowedMimeTypesForField) {
        return cb(
            new ApiError(
                500,
                `Invalid field ${file.fieldname}. Please ensure you are uploading to a valid field.`
            ),
            false
        );
    }

    if (allowedMimeTypesForField.includes(file.mimetype)) {
        cb(null, true);
    } else {
        cb(
            new ApiError(
                400,
                `Invalid file type for ${file.fieldname}. Allowed file types for ${file.fieldname} are ${allowedMimeTypesForField.map((type) => type.split("/")[1]).join(", ")}.`
            ),
            false
        );
    }
};

export const upload = multer({
    storage,
    fileFilter,
});

Is it possible to do this with my given code. Or I have to make 2 multer instances one for image and one for video.


Solution

  • You don't need fileFilter function because we can get file.size there so we can simply convert this into middleware.

    Refer this issue in multer.

    In the middleware you can apply the file size validation like below.

    function FileValidationMiddleware(req,_,next){
        const { files } = req;
        
        const types = {
            'image': {
                maxSize: 10 ,//MB
                mimetype: [
                    "image/png",
                    "image/jpeg",
                    "image/webp",
                    "image/svg+xml",
                ]
            },
            'video': {
                maxSize: 100, //MB
                mimetype: [
                    "video/mp4", 
                    "video/webm"
                ]
            }
        }
    
        const allowedMimeTypesForFields = {
            avatar: 'image',
            coverImage: 'image',
            videoFile: 'video',
            thumbnail: 'image',
            featuredImage: 'image',
        };
    
        Object.values(files).forEach(([file]) => {
            const fileSize = file.size / 1024 / 1024; // in MB
    
            const allowedMimeTypesForField = allowedMimeTypesForFields[file.fieldname];
        
            if (!allowedMimeTypesForField) {
                throw new ApiError(
                    500,
                    `Invalid field ${file.fieldname}. Please ensure you are uploading to a valid field.`
                );
            }
        
        
            if (types[allowedMimeTypesForField].mimetype.includes(file.mimetype)) {
                if(fileSize > types[allowedMimeTypesForField].maxSize){
                    throw new ApiError(400,`Invalid file size for ${file.fieldname}. Allowed maximum file size is ${types[allowedMimeTypesForField].maxSize} MB`);
                }
            } else {
                throw new ApiError(
                    400,
                    `Invalid file type for ${file.fieldname}. Allowed file types for ${file.fieldname} are ${types[allowedMimeTypesForField].mimetype.map((type) => type.split("/")[1]).join(", ")}.`
                );
            }
        });
    
        next();
    
    }