Search code examples
reactjsnode.jstypescriptmulter

Issue with multer upload files


I want to upload images on the website built on React. I have issue with backend Node.js code.

Code:

const multer = require("multer");

// Check if the directory exists, if not, create it
const directory = path.join(__dirname, "../src/assets/img/images");

if (!fs.existsSync(directory)) {
    fs.mkdirSync(directory);
}

console.log("Directory: ", directory);

// Set up multer storage and file filter
// Set up multer middleware
const uploadMulter = multer({
    storage: multer.diskStorage({
        destination: (req, file, cb) => {
            cb(null, directory); // Save files to the "src/assets/img/images" directory
        },
        filename: (req, file, cb) => {
            cb(null, file.originalname); // Use the original file name
        }
    }),
    fileFilter: (req, file, cb) => {
        // Accept only image files
        if (file.mimetype.startsWith("image/")) {
            console.log("Success!!!");
            cb(null, true);
        } else {
            console.log("Error!!!!");
            cb(new Error("Invalid file type. Only image files are allowed."));
        }
    }
});

interface FileContent {
  fileName: string;
  content: string;
  type: string;
}

// Route for file upload
app.post("/upload/images", uploadMulter.array("files[]"), (req, res) => {
    // Retrieve the uploaded files from req.files array
    const files = req.files;
    // You can now process the uploaded files (e.g., save to database, manipulate, etc.)
    // For example, if you want to read the content of each file:
    const fileContents: FileContent[] = [];
    files.forEach((file) => {
        console.log("FileError: ", file.fileError);
        console.log("file.fileError.message: ", file.fileError.message);
        const data = fs.readFileSync(file.path, "binary");
        fileContents.push({fileName: file.originalname, content: Buffer.from(data, "binary").toString("base64"), type: file.mimetype}); //, fileError: error
    });

    // Send response
    res.status(200).json({files: fileContents});
});

At this moment, it just prints to the server console. I want to print this error to a user in case a user selects the text file instead of an image:

Invalid file type. Only image files are allowed.

But it fails for me with this error: POST http://localhost:3000/upload/images 500 (Internal Server Error).

Full error log:

Error: Invalid file type. Only image files are allowed.
    at fileFilter (C:\wamp64\www\insurance_site\build_server\server.js:4609:5399)
    at wrappedFileFilter (C:\wamp64\www\insurance_site\node_modules\multer\index.js:44:7)
    at Multipart.<anonymous> (C:\wamp64\www\insurance_site\node_modules\multer\lib\make-middleware.js:107:7)
    at Multipart.emit (events.js:400:28)
    at Multipart.emit (domain.js:475:12)
    at HeaderParser.cb (C:\wamp64\www\insurance_site\node_modules\busboy\lib\types\multipart.js:358:14)
    at HeaderParser.push (C:\wamp64\www\insurance_site\node_modules\busboy\lib\types\multipart.js:162:20)
    at SBMH.ssCb [as _cb] (C:\wamp64\www\insurance_site\node_modules\busboy\lib\types\multipart.js:394:37)
    at feed (C:\wamp64\www\insurance_site\node_modules\streamsearch\lib\sbmh.js:219:14)
    at SBMH.push (C:\wamp64\www\insurance_site\node_modules\streamsearch\lib\sbmh.js:104:16)

Any ideas how to return the proper message back to a user?


Solution

  • Ok. I have fixed this issue by removing fileFilterfrom multer. Also, I added checks for image types in the app.post("/upload/images"... method. This will not prevent file from upload to a server but I can display the error message "Invalid file type. Only image files are allowed." to a user and remove file if it's not an image.

    Code:

    const uploadMulter = multer({
        storage: multer.diskStorage({
            destination: (req, file, cb) => {
                cb(null, directory);
            },
            filename: (req, file, cb) => {
                cb(null, file.originalname);
            }
        })
    });
    
    app.post("/upload/images", uploadMulter.array("files[]"), (req, res) => {
        const files = req.files;
        const filteredFiles = files.filter(file => file.mimetype.startsWith("image/"));
        
        if (filteredFiles.length !== files.length) {
            files.forEach((file) => {
              fs.unlink(file.path, (error) => {});
            });
    
            return res.status(400).json({error: "Invalid file type. Only image files are allowed."});
        }
    
        const fileContents: FileContent[] = [];
        
        filteredFiles.forEach((file) => {
            const data = fs.readFileSync(file.path, "binary");
            fileContents.push({fileName: file.originalname, content: Buffer.from(data, "binary").toString("base64"), type: file.mimetype, path: file.path, successMsg: "Image(s) uploaded successfully."});
        });
    
        res.status(200).json({files: fileContents});
    });
    

    It works well. This issue is resolved.