I have an application to sell cars, I want to upload each car images to a separate folder inside this path country/city
.
For example:
Let's say there is a car in Berlin, Germany. When adding this car I am saving the images to this folder Germany/Berlin
.
Now I don't want to put all the images of the cars in Berlin, Germany inside the same folder, I want to make a separate folder for each car in Berlin, Germany.
My first approach was to count the number of folders inside the base path and then make a new one by adding one to the number.
For example:
Let's say there are 5 folders inside Germany/Berlin
which is the base path for our example
The path would now be like this "Germany/Berlin/6". The problem is that it's making a different folder for each image.
For example:
Let's say there are 5 folders inside Germany/Berlin
and there are 4 images for the car that is being uploaded.
The first image goes inside Germany/Berlin/6
.
The second image goes inside Germany/Berlin/7
.
The third image goes inside Germany/Berlin/8
.
The fourth image goes inside Germany/Berlin/9
.
I think the problem is that the destination function is being executed for each one of the images.
How can I solve this?
Here's My Code:
const multer = require("multer");
const path = require("path");
const fs = require("fs");
const { onlyImagesAreAllowed } = require("./error_codes");
function uploadFiles(filePath) {
const storage = multer.diskStorage({
destination: (req, _file, cb) => {
const data = req.body;
let dirsCount = 1;
let dirPath = `images/${filePath}/${data.country}/${data.city}`;
const exists = fs.existsSync(dirPath);
if (!exists) {
fs.mkdirSync(dirPath, { recursive: true });
}
if (exists) {
dirsCount =
fs
.readdirSync(dirPath, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name).length 1;
}
dirPath = `${dirsCount}`;
fs.mkdirSync(dirPath, { recursive: true });
cb(null, dirPath);
},
filename: (_req, file, cb) => {
const uniqueSuffix = Date.now() "-" Math.round(Math.random() * 1e9);
cb(null, file.fieldname "-" uniqueSuffix);
},
});
const fileFilter = (req, file, callback) => {
const acceptedTypes = file.mimetype.split("/");
if (acceptedTypes[0] === "image") {
callback(null, true);
} else {
callback(null, false);
callback(new Error(onlyImagesAreAllowed));
}
};
const limits = {
fileSize: 20 * 1024 * 1024,
};
return multer({
storage: storage,
fileFilter: fileFilter,
limits: limits,
});
}
module.exports = {
uploadFiles,
};
And the usage is like this:
const express = require("express");
const carsController = require("./cars.controller");
const { uploadFiles } = require("../../utils/upload_files");
const router = express.Router();
router.post(
"/addCar",
uploadFiles("cars").array("carGallery", 12),
carController.addCar
);
module.exports = router;
CodePudding user response:
If req
is the same for each image file (which I assume it is), you can add a custom property to it containing the directory. If that property doesn't exist, run your logic to determine the next directory name; if it does exist, reuse it.
Something like this:
destination: (req, _file, cb) => {
// check if we already determine an upload path for this request
if (req._upload_path) {
return cb(null, req._upload_path);
}
// no, we didn't, so determine the next upload path
const data = req.body;
let dirsCount = 1;
...
[the rest of your logic]
...
fs.mkdirSync(dirPath, { recursive: true });
// store the upload path we just determined
req._upload_path = dirPath;
cb(null, dirPath);
},
As an aside: if you don't really need consecutively numbered directories, I would suggest using a unique id (something like a UUID) instead (it'll be much quicker and less prone to race conditions).