req is "undefined" in one middleware and not in another - node.js

I am trying to use sharp in my MERN application, I sent a request from my frontend and it is undefined in my sharp middleware but if I get rid of the sharp middleware the req is defined later on. If I log the request in createCountry, the body is defined, if I log it in convertToWebP, it is not.
the route is the one that says "/new" below:
const express = require("express");
const router = express.Router();
const { storage } = require("../imageupload/cloudinary.js");
const multer = require("multer");
const {
getCountry,
createCountry,
getCountries,
updateCountry,
deleteCountry,
getAllCountries,
} = require("../controllers/country.js");
const {convertToWebP} = require('../middlewares/toWebP')
const { isLoggedIn, authorizeCountry, validateCountry } = require("../middlewares/auth");
const catchAsync = require("../utils/catchAsync");
const ExpressError = require("../utils/ExpressError");
const upload = multer({ storage: storage });
router.get("/", getCountries);
router.get('/getAll', getAllCountries);
router.post("/new", isLoggedIn, converToWebP, upload.array("images"), createCountry);
router.get("/:countryId", getCountry);
router.patch("/:countryId", validateCountry, authorizeCountry, upload.array("images", 8), updateCountry);
router.delete("/:countryId", authorizeCountry, deleteCountry);
module.exports = router;
the code for create country is here:
exports.createCountry = async (req, res) => {
const { name, description, tags, location, cjLink } = req.body;
const creator = req.user._id;
const images = req.files.map((file) => {
return { image: file.path, publicId: file.filename };
});
try {
const geoData = await geocoder
.forwardGeocode({
query: req.body.location,
limit: 1,
})
.send();
const geometry = geoData.body.features[0].geometry;
const country = new Country({
name,
description,
tags,
creator,
location, //: //geometry
geometry,
url: '',
cjLink: cjLink,
});
const overall = new Overall({
name,
description,
tags,
creator,
location, //: //geometry
geometry,
url: '',
cjLink: cjLink,
});
country.images.push(...images);
country.headerImage.push(...images);
const data = await country.save();
overall.url = `/country/${data._id}`
data.url = `/country/${data._id}`
overall.save();
data.save();
return res.status(201).json(data);
} catch (error) {
return console.log("error during create country", error);
}
};
And lastly the code for the convertToWebP is here:
const sharp = require("sharp");
const { cloudinary } = require("../imageupload/cloudinary");
exports.convertToWebP = async (req, res, next) => {
try {
req.files = await Promise.all(req.files.map(async (file) => {
const buffer = await sharp(file.buffer)
.toFormat('webp')
.toBuffer();
return { ...file, buffer, originalname: `${file.originalname}.webp` };
}));
next();
} catch (error) {
res.status(500).json({ message: error.message });
}
};
Any help is appreciated! I tried console.log as described above, I tried to change the order of the middleware and that does not work either, and I tried logging the req.body directly from the route and it came up as an empty object

You cannot acces req.files before you use multer middleware
You have to reorder
router.post("/new", isLoggedIn, upload.array("images"), converToWebP, createCountry);

Related

Nodejs how to use Multer on function

I have created controller, routes and functions base api. What issue I am getting is how can I use Multer on function.
I have function like this
const { Users } = require('../models/user');
const { Company } = require('../models/company');
const { Jobs } = require('../models/job');
var mongoose = require('mongoose');
const multer = require('multer');
const FILE_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpeg',
'image/jpg': 'jpg'
};
const storage = multer.diskStorage({
destination: function (req, file, cb) {
const isValid = FILE_TYPE_MAP[file.mimetype];
let uploadError = new Error('invalid image type');
if (isValid) {
uploadError = null;
}
cb(uploadError, 'public/uploads');
},
filename: function (req, file, cb) {
const fileName = file.originalname.split(' ').join('-');
const extension = FILE_TYPE_MAP[file.mimetype];
cb(null, `${fileName}-${Date.now()}.${extension}`);
}
});
const uploadOptions = multer({ storage: storage });
const createJob = function async(req, res) {
const job = new Jobs({
jobTitle: req.body.jobTitle,
jobDescription: req.body.jobDescription,
jobImage: req.body.userType,
jobType: req.body.jobType,
jobNumberOfPeople: req.body.jobNumberOfPeople,
jobHireTime: req.body.jobHireTime,
jobMinPay: req.body.jobMinPay,
jobMaxPay: req.body.jobMaxPay,
jobday: req.body.jobday,
jobRecieveApplication: req.body.jobRecieveApplication,
jobSubmitResume: req.body.jobSubmitResume,
jobApplicationDeadline: req.body.jobApplicationDeadline,
jobCommunationSetting: req.body.jobCommunationSetting,
jobMessageSetting: req.body.jobMessageSetting,
companyID: req.body.companyID,
});
try {
const jobsave = await job.save();
res.status(200).json({ success: true, data: jobsave })
} catch (err) {
if (err.name === 'ValidationError') {
console.error(Object.values(err.errors).map(val => val.message))
return res.status(400).json({ success: false, message: Object.values(err.errors).map(val => val.message)[0] })
}
res.status(400).json({ success: false, message: err })
}
};
module.exports = { createJob };
and routes like this
const express = require('express');
const router = express.Router();
const userController = require('../controllers/user');
const jobController = require('../controllers/jobs');
router.post('/createJob', jobController.createJob);
module.exports = router;
now I need to add uploadOptions.single('jobImage') in function
I am doing like this const createJob =
const createJob = uploadOptions.single('jobImage'), async(req, res) => { };
Its showing this error on comma don't know why
Its working directly on router but I need to do In function
You can use this:
module.exports = {
createJob : [ uploadOptions.single('jobImage'), createJob ]
};

delete image with nodejs

I want to delete the old image from images folder when I update the product in NodeJS, product is updating, but old image is not deleting from the folder.
index.js
app.use(bodyParser.json({extended:true}))
app.use(bodyParser.urlencoded({extended:true}))
app.use(cors())
app.use('/', route)
app.use('/images', express.static('images'))
function
export const updateProduct = async (req, res)=>
{
try{
let image
const oldProduct = await Product.findOne({_id:req.params.id})
const {name,price,quantity,category} = req.body
if(req.file)
{
image = req.file.filename
const oldImageUrl= `/images/${oldProduct.image}`
// this is url of the old image http://localhost:2001/images/1629969633380_r.png
await fs.unlinkSync(oldImageUrl)
}else{
image = oldProduct.image
}
const productToUpdate = new Product({name,category,quantity,price,image})
await Product.updateOne({_id:req.params.id},productToUpdate)
res.status(200).json('product Updated')
}catch(error)
{
res.status(404).json({message:error.message})
}
}
Based on this bit of code:
app.use('/images', express.static('images'))
You should try to delete the image relative to the app folder.
const oldImageUrl= `images/${oldProduct.image}`
Or even better yet, use the path module.
const { join } = require('path');
...
const oldImageUrl = join(__dirname, 'images', oldProduct.image);
router.post('/update/:id', upload.single("file"), async (req, res) => {
let data = {
name: req.body.name,
price: req.body.price,
quantity: req.body.quantity,
discount: req.body.discount,
discription: req.body.discription,
file: req.file.filename
}
const oldProduct = await products.findOne({ _id: req.params.id });
const result = await products.findByIdAndUpdate(req.params.id, data,);
fs.unlink('./public/image/' + oldProduct.file, function (err) {
if (err && err.code == 'ENOENT') {
// file doens't exist
console.info("File doesn't exist, won't remove it.");
} else if (err) {
// other errors, e.g. maybe we don't have enough permission
console.error("Error occurred while trying to remove file");
} else {
console.info(`removed`);
}
});
res.redirect('/listProducts');
})

Uploading Multiple Images to Cloudinary Using Multer and Express

I have this express backend for adding products to a database, now I have configured it to take product image then name, price, type, and color and it has worked very well so far. But now I am trying to make it so that it can take not one image but up to four but I've been running into issues. The initial code for the single image is as follows
First the config for Cloudinary
const express = require("express");
const cloudinary = require("cloudinary").v2;
const { CloudinaryStorage } = require("multer-storage-cloudinary");
const multer = require("multer");
const verify = require("../routes/verifyToken");
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET,
});
const storage = new CloudinaryStorage({
cloudinary: cloudinary,
params: {
folder: "Shoes",
format: async (req, file) => {
"jpg", "png";
}, // supports promises as well
public_id: (req, file) => {
console.log(
new Date().toISOString().replace(/:/g, "-") + file.originalname
);
return (
new Date().toISOString().replace(/:/g, "-") + file.originalname
);
},
},
});
const parser = multer({ storage: storage });
Then now the post request to post the shoes(product).
router.post("/post/menshoe", verify,parser.single("shoeImage"), async (req, res) => {
// console.log(req.file);
if (!req.file) return res.send("Please upload a file");
// console.log(req.file); // to see what is returned to you
const image = {};
console.log(req.file)
const shoeUpload = new MenShoe({
shoeImage: req.file.path,
name: req.body.name,
type: req.body.type,
price: req.body.price,
color: req.body.color,
});
console.log(shoeUpload);
try {
const shoe = await shoeUpload.save();
res.json({ msg: "Shoe uploaded", success: true, shoe });
} catch (err) {
console.log(err);
res.json({
msg: "Failed to upload",
success: false,
err,
});
}
}
);
I would like to point out that I've tried to research for a way but each answer that I have encountered is using a completely different way to post images and I am seriously trying to avoid starting to write this from scratch as I have written a lot of code exactly like this. I would really appreciate it if anyone can help me achieve this with just a few tweaks to this code.
Thanks in advance
In your model dir;
const shoeSchema = new mongoose.Schema({
// other properties here
shoeImage: [{
type: String,
required: true // it could be optional
}],
});
module.exports = Shoe = mongoose.model('product', shoeSchema);
Inside your post route,
router.post("/post/menshoe", verify,parser.array("shoeImage", 4), async
(req, res) => {
const { name, type, price, color } = req.body;
try {
let shoeUpload = new MenShoe({
name,
type,
price,
color
});
if (req.files) { // if you are adding multiple files at a go
const imageURIs = []; // array to hold the image urls
const files = req.files; // array of images
for (const file of files) {
const { path } = file;
imageURIs.push(path);
};
shoeUpload['shoeImage'] = imageURIs; // add the urls to object
await shoeUpload.save();
return res.status(201).json({ shoeUpload });
}
if (req.file && req.file.path) {// if only one image uploaded
shoeUpload['shoeImage'] = req.file.path; // add the single
await shoeUpload.save();
return res.status(201).json({ shoeUpload });
};
// you could save here without the image
...
return res.status(400).json({ // in case things don't work out
msg: 'Please upload an image'
});
}catch {
console.error("server error occur", error.message);//only in dev
return res.status(500).send("Server Error Occurred");
}
});

how to store CSV data into mongoDB using nodeJS

const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({ storage });
const csv = require('csvtojson');
public fileUpload = async (req, res): Promise<any> => {
const fileBuffer = req.files[0].buffer.toString();
res.status(200).json({ fileBuffer });
csv({
noheader:true,
output: 'csv',
}).fromString(fileBuffer)
.then((csvRow) => {
csvRow.shift();
this.create(req , res, csvRow);
});
}
//above lines are my code i am getting data in below format i want to
store this data in mongoDB
>[
> [Deepak ,4434444554,454466656,Tata],
> [Naren,8837377373,343443434,Tata]
>]
//this is my create method here i want to match data with schema and store it into database
public create (req, res, object) {
console.log('hi-----------', object);
const { partner_name, mobile_no, alternate_no, vehicle_type } =
object;
try {
const lead = new leadModels({
alternate_no, mobile_no, partner_name, vehicle_type,
});
const newLead = lead.save();
res.status(200).json({ message: 'lead created successfully' });
} catch (err) {
res.status(500).json({ message: appData.internalServerError });
}
}

How to upload file using Koa?

I am setting up a new server and want to support advanced upload function. Firstly I need to validate file (filetype, filesize, maxcount), and finally upload it to some destination. I tried something with koa-multer but I cannot get multer validation errors.
multer.js
const multer = require('koa-multer')
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/uploads/')
},
filename: function (req, file, cb) {
var fileFormat = (file.originalname).split('.')
cb(null, file.fieldname + '_' + Date.now() + '.' + fileFormat[fileFormat.length - 1])
}
})
const fileFilter = (req, file, cb) => {
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true)
} else {
cb(new Error('This is not image file type'), false)
}
}
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 1,
files: 5
},
fileFilter: fileFilter
})
module.exports = upload
router.js
const Router = require('koa-router')
const multer = require('../middlewares/multer')
const auth = require('../middlewares/auth')
const controller = require('../controllers').userController
const schemas = require('../schemas/joi_schemas')
const validation = require('../middlewares/validation')
const router = new Router()
const BASE_URL = `/users`
router.post(BASE_URL, auth , validation(schemas.uPOST, 'body'), controller.
addUser)
router.put(`${BASE_URL}/:id`, auth , multer.single('logo')(ctx, (err) => {
if (err) {
ctx.body = {
success: false,
message: 'This is not image file'
}
}
}), controller.editUser)
router.delete(`${BASE_URL}/:id`, auth , controller.deleteUser)
module.exports = router.routes()
How can I solve upload problem this in best way for long term maintain of code?
The simplest approach for uploading files is the following (assume the form has a file upload field called avatar:
const Koa = require('koa')
const mime = require('mime-types')
const Router = require('koa-router')
const koaBody = require('koa-body')({multipart: true, uploadDir: '.'})
const router = new Router()
router.post('/register', koaBody, async ctx => {
try {
const {path, name, type} = ctx.request.files.avatar
const fileExtension = mime.extension(type)
console.log(`path: ${path}`)
console.log(`filename: ${name}`)
console.log(`type: ${type}`)
console.log(`fileExtension: ${fileExtension}`)
await fs.copy(path, `public/avatars/${name}`)
ctx.redirect('/')
} catch(err) {
console.log(`error ${err.message}`)
await ctx.render('error', {message: err.message})
}
})
Notice that this example uses Async Functions which allows for clean code with a standard try-catch block to handle exceptions.
koa middleware is like a nested callback, you should catch "next()" not after multer
router.put(`${BASE_URL}/:id`, auth , async (ctx, next) => {
try{
await next()
} catch(err) {
ctx.body = {
success: false,
message: 'This is not image file'
}
}
}, multer.single('logo'), controller.editUser)
but you do this, it will catch controller.editUser errors too which not been caught by controller itself.
You can use one of two options:
The first is by adding callback function to the end of the route.
const multer = require('#koa/multer')
//Options to limit file size and file extension
const upload = multer({
dest: '../avatars',
limits: {
fileSize: 1024*1024
},
fileFilter(ctx, file, cb) {
if (!file.originalname.match(/\.(jpg|jpeg|png)$/)) {
return cb(new Error('Please upload a World document'))
}
cb(undefined, true)
}
})
//The last callback should handle the error from multer
router
.post('/upload', upload.single('upload'), async (ctx) => {
ctx.status = 200
}, (error, ctx) => {
ctx.status = 400
ctx.body = error.message
})
})
The second option is by adding try/ catch before calling multer middleware:
router
.post('/upload', async (ctx, next) => {
try {
await next()
ctx.status = 200
} catch (error) {
ctx.status = 400
ctx.body = error.message
}
}, upload.single('upload'), async ctx => {ctx.status = 200})
In last case if exception will thrown in multer, it will be handled by try/catch in previous await next()

Resources