- 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;
- 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 },
])
);
- 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,
}
- 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
};
- 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,
});
}
- 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,
});
}
- 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;
- To remove a single file, use async in controller
await removeFile(item);
- To remove a multiple files, use async in controller
imagePaths.map(async (item) => {
await removeFile(item);
})