My first time to handle multiple files in Node.js and Multer library. I have multiple path with file. For example employee information, product, etc. Some paths have a single field for input file but some paths have more than two fields to input file.
The conditionals:
1. I should handle the files into the best suitable directory,
2. Rename each file to unique name in the directory.
3. Return each new name to the main function to record into database and reusable.
This is what I have tried. And I also attached a link to git project including example of request image.
git project: https://github.com/Kicks-Me/multer-file-handle.git
import * as multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { v4 as uuidv4 } from 'uuid';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const mimeTypes = {
"image/png":"png",
"image/jpeg":"jpg",
"image/jpg":"jpg",
"image/gif":"gif",
"video/mp4":"mp4",
"audio/mpeg":"mp3",
"audio/wav":"wav",
"application/pdf":"pdf",
"application/vnd.ms-excel":"xls",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":"xlsx"
};
const accessFilePath = (fileType, reqpath) => {
let dest = "";
let pathType = "uploads/Others";
if(reqpath === "/location")
{
pathType = "uploads/locations";
}
else if(reqpath === "/employee")
{
pathType = "uploads/employees";
}
else if(reqpath === "/product")
{
pathType = "uploads/products";
}
dest =
mimeTypes[fileType] === "mp4"
? path.join(__dirname, "..", pathType, "video")
: mimeTypes[fileType] === "pdf"
? path.join(__dirname, "..", pathType, "documents", "pdf")
: mimeTypes[fileType] === "xls" || mimeTypes[fileType] === "xlsx"
? path.join(__dirname, "..", pathType, "documents", "excel")
: mimeTypes[fileType] === "mp3" || mimeTypes[fileType] === "wav"
? path.join(__dirname, "..", pathType, "audio")
: mimeTypes[fileType] === "gif"
? path.join(__dirname, "..", pathType, "gif")
: path.join(__dirname, "..", pathType, "image");
return dest;
};
const multerConfig = {
storage: multer.diskStorage({
destination: (req, file, callback) => {
let dest = "";
const fileType = file.mimetype;
dest = accessFilePath(fileType, req?.path);
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest, { recursive: true });
}
callback(null, dest);
},
filename: (req, file, callback) => {
const ext = mimeTypes[file.mimetype];
callback(null, `${uuidv4()}.${ext}`);
},
}),
fileFilter: (req, file, callback) => {
const ext = mimeTypes[file.mimetype];
ext === "png" ||
ext === "jpg" ||
ext === "gif" ||
ext === "wav" ||
ext === "mp3" ||
ext === "wav" ||
ext === "mp4" ||
ext === "pdf" ||
ext === "xls" ||
ext === "xlsx"
? callback(null, true)
: callback(null, false);
},
};
export const upload = multer.default(multerConfig);
// Middleware to handle multiple files with different keys
export const handleMultipleFiles = (req, res, next) => {
const newImages = {};
// Process files attached to 'Profile' key
if (req.file) {
newImages.profile = req.file.filename;
}
// Process files attached to 'qr' key
if (req.files && req.files.length > 0) {
newImages.qr = req.files.map((file) => file.filename);
}
req.newImages = newImages;
next();
};
Here is the route
import Route from 'express';
import { verifyJWT } from '../helper/jwt.js';
import { handleMultipleFiles, upload } from '../helper/multer.js';
const route = Route();
route.post('/upload',verifyJWT, upload.single('Profile'), upload.array('Images', 5),
handleMultipleFiles, (req, res) => {
const profileImage = req.newImages.profile;
const qrImages = req.newImages.qr;
return res.json({ profileImage, qrImages });
});
export default route;
From above code, I faced with some error:
MulterError: Unexpected field
at wrappedFileFilter (/Users/tplussmacbookpro2/Desktop/kkkk/file-
handle/node_modules/multer/index.js:40:19)
at Multipart.<anonymous> (/Users/tplussmacbookpro2/Desktop/kkkk/file-
handle/node_modules/multer/lib/make-middleware.js:107:7)
at Multipart.emit (node:events:514:28)
at HeaderParser.cb (/Users/tplussmacbookpro2/Desktop/kkkk/file-
handle/node_modules/busboy/lib/types/multipart.js:358:14)
You can review the attached project in github for more detail.
Thank you in advance.
Uploading multiple files with fieldnames that can be optional can be handled with .fields() method.
So, setup fieldnames for processing:
upload.fields([
{
name: 'Profile'
maxCount: 1,
},
{
name: 'Images',
maxCount: 5,
},
])
and then, req.files
will contain object with corresponding fieldnames (if provided, if not, the property won't be present), for example, if both fieldnames are present:
// req.files
{
Images: [file1, file2..],
Profile: [file]
}
so in the files handler, you simply check the presence of those fieldnames, and assign filenames accordingly:
// Process files attached to 'Profile' key
if(req.files.Profile) {
newImages.profile = req.files.Profile[0].filename;
}
// Process files attached to 'qr' key
if(req.files.Images) {
newImages.qr = req.files.Images.map((file) => file.filename);
}
full modified code:
route:
route.post('/upload',verifyJWT, upload.fields([
{
name: 'Profile'
maxCount: 1,
},
{
name: 'Images',
maxCount: 5,
},
]), handleMultipleFiles, (req, res) => {
const profileImage = req.newImages.profile;
const qrImages = req.newImages.qr;
return res.json({ profileImage, qrImages });
});
handler:
// Middleware to handle multiple files with different keys
export const handleMultipleFiles = (req, res, next) => {
const newImages = {};
// Process files attached to 'Profile' key
if(req.files.Profile) {
newImages.profile = req.files.Profile[0].filename;
}
// Process files attached to 'qr' key
if(req.files.Images) {
newImages.qr = req.files.Images.map((file) => file.filename);
}
req.newImages = newImages;
next();
};