0
  1. configure the multer
  2. multer configure as global middleware
  3. helper for re-use to get uploaded image or images path
  4. transfer upload file to a dedicated folder
  5. To upload single file and transfer to dedicated folder
  6. To upload multiple files and transfer to dedicated folder
  7. To remove a file reusable helper
  8. To remove a single file
  9. To remove a multiple files
import multer from 'multer'
import path from 'path'
import fs from 'fs'

const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        const uploadDir = path.join(__dirname, "../../public/files/");
        if (!fs.existsSync(uploadDir)) {
            fs.mkdirSync(uploadDir, { recursive: true });
        }
        cb(null, "public/files");
    },
    filename: (req, file, cb) => {
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
        cb(null, uniqueSuffix + '-' + file.originalname);
    },
});

const upload = multer({
    storage: storage,
    fileFilter: (req, file, cb) => {
        const supportedFile = /jpg|jpeg|png|webp|svg|pdf/;
        const extension = path.extname(file.originalname);

        if (supportedFile.test(extension)) {
            cb(null, true);
        } else {
            cb(new Error('Must be a jpg/png/jpeg/webp/svg file'), false);
        }
    },
});


export default upload;

1 Answer 1

0
  1. configure multer
import multer from 'multer';
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import { dirname } from 'path';

// Get __dirname equivalent in ES Modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        const uploadDir = path.join(__dirname, "../../public/files/");
        if (!fs.existsSync(uploadDir)) {
            fs.mkdirSync(uploadDir, { recursive: true });
        }
        cb(null, "public/files");
    },
    filename: (req, file, cb) => {
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
        cb(null, uniqueSuffix + '-' + file.originalname);
    },
});

const upload = multer({
    storage: storage,
    fileFilter: (req, file, cb) => {
        const supportedFile = /jpg|jpeg|png|webp|svg|pdf/;
        const extension = path.extname(file.originalname).toLowerCase();

        if (supportedFile.test(extension)) {
            cb(null, true);
        } else {
            cb(new Error('Must be a jpg/png/jpeg/webp/svg/pdf file'), false);
        }
    },
});

export default upload;
  1. use multer configure as global middleware in your main file (eg: index.js / server.js) so, every request data have to send with form-data.
  // multer configure
    app.use(
        upload.fields([
            { name: "single", maxCount: 1 },
            { name: "multiple", maxCount: 10 },
        ])
    );
  1. I am using a helper for re-using to get uploaded image or images path
// single image file upload -> image path
const returnSingleFilePath = async (files, field = 'single') => {

    let filePath;

    if (files && Object.keys(files).length > 0) {
        if (Array.isArray(files)) {
            filePath = files[0].path;
        } else {
            filePath = files[field]?.[0]?.path;
        }
    }

    return filePath;
}

// mutiple image file upload -> image paths
const returnMultipleFilePath = async (files, field = 'multiple') => {

    let imagesPaths = [];

    if (files && Object.keys(files).length > 0) {
        files[field].map((item) => {
            imagesPaths.push(item.path);
        })
    }

    return imagesPaths;
}

export default {
    returnSingleFilePath,
    returnMultipleFilePath,
}
  1. Also, I am using another helper to transfer upload file to a dedicated folder after uploading. In this help again for re-use I am using two function to transfer single or multiple image. Here, in root there will be exists or create public folder then inside the folder transfer files folder will be automatically create and transfer the file.
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';

// Get __dirname equivalent in ES Modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Function to move file to specific folder
const singleFileTransfer = (filePath, destinationFolder) => {
    const fileName = path.basename(filePath);
    const newFilePath = path.join(__dirname, '../../public', destinationFolder, fileName);
    const fileUrl = `public/${destinationFolder}/${fileName}`; // the new URL of the file

    // Check if the destination folder exists, if not, create it
    if (!fs.existsSync(path.dirname(newFilePath))) {
        fs.mkdirSync(path.dirname(newFilePath), { recursive: true });
    }

    // Move the file to the destination folder
    fs.rename(filePath, newFilePath, (err) => {
        if (err) {
            console.log(`Error moving file: ${err}`);
        } else {
            console.log(`File moved successfully to ${newFilePath}`);
        }
    });

    return fileUrl;
}

// Function to move files to specific folder
const multipleFilesTransfer = async (imagePaths, destinationFolder) => {
    const paths = [];

    imagePaths.map((item) => {
        const newPath = singleFileTransfer(item, destinationFolder);
        paths.push(newPath);
    });

    return paths;
}

export {
    singleFileTransfer,
    multipleFilesTransfer
};
  1. To upload single file and transfer to dedicated folder
const Controller = async (req, res) => {

    const body = req.body && req.body.data ? JSON.parse(req.body.data) : {};

    // getting the images path
    if (req.files && Object.keys(req.files).length > 0) {
        if (req.files.single) {
            const imagePath = await returnFilePath.returnSingleFilePath(req.files, 'single');

            // transfer images to new folder and assign new paths
            if (imagePath) {
                const newImagePath = await singleFileTransfer(imagePath, "folderName")
                body.logo = newImagePath;
            }
        }
    }

    sendResponse(res, {
        statusCode: 200,
        success: true,
        message: 'Api called successfully!',
        data: body,
    });
}
  1. To upload multiple files and transfer to dedicated folder
const Controller = async (req, res) => {

    const body = req.body && req.body.data ? JSON.parse(req.body.data) : {};

    // getting the images path
    if (req.files && Object.keys(req.files).length > 0) {
        // upload images
        const imagePaths = await returnFilePath.returnMultipleFilePath(req.files);

        // transfer images to new folder and assign new paths
        let newImagePaths = [];
        if (imagePaths.length) {
            newImagePaths = await multipleFilesTransfer(imagePaths, "folderName")
            body.photos = newImagePaths;
        }
    }

    sendResponse(res, {
        statusCode: 200,
        success: true,
        message: 'Api called successfully!',
        data,
    });
}
  1. To remove a file I am using a new helper
import fs from 'fs';

const removeFile = async (imgPath) => {
    if (fs.existsSync(imgPath)) {
        fs.unlinkSync(imgPath);
        console.log(`File ${imgPath} deleted successfully`);
    } else {
        console.log(`File ${imgPath} does not exist`);
    }
}

export default removeFile;
  1. To remove a single file, use async in controller
 await removeFile(item);
  1. To remove a multiple files, use async in controller
 imagePaths.map(async (item) => {
     await removeFile(item);
 })

Not the answer you're looking for? Browse other questions tagged or ask your own question.