Handling Multer Errors - node.js

Please I would like to handle all errors from multer while uploading files to cloudinary and create my own custom error messages. How would I do that?
This is the cloudinary and multer configuration
const storage = new CloudinaryStorage({
cloudinary: cloudinary,
params: {
folder: "Products",
format: async (req, file) => {
"jpg", "png";
},
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 });
And this is the route
router.post("/addProduct", middleware.IsMerchant, parser.array("Images", 3), async(req, res)=>{
const { ProductName, Description, Category, Price} = req.body
try {
let product = new Product({
ProductName,
Description,
Category,
Price
})
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);
};
product['Images'] = imageURIs;
product.Owner.id = req.user._id
product.Owner.username = req.user.username
await product.save();
console.log("added new product")
req.flash("success", "You did it")
res.status(201).redirect("back");
}
} catch (error) {
console.log(error)
}
})

Related

Upload video/image file data in node.js and store it in MongoDB

I want to store Image/Video data that user posts into a mongoDB database but it is getting saved as an empty array.
The Multer Configuration:
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: "./uploads/",
filename: function (req, file, cb) {
cb(
null,
file.fieldname + "-" + Date.now() + path.extname(file.originalname)
);
},
});
const upload = multer({
storage,
}).array("media");
module.exports = upload;
The createPost route to upload a new post that can be type of anything like plain-text, Images or videos[Media]:
exports.createPost = async (req, res) => {
try {
const user = await User.findById(req.user._id);
if (!user) return res.status(401).json({ message: "No user found" });
const { media, hashTags, postStatus } = req.body;
const mentions = await User.find(
{ User_name: req.body.mentions },
{ User_name: 1, _id: 0 }
);
if (
req.body.mentions &&
(!Array.isArray(mentions) || mentions.length === 0)
) {
return res.status(404).send("Please enter valid user name");
}
let payLoad = {
userId: req.user._id,
media,
hashTags,
postStatus,
};
if (mentions.length > 0) {
payLoad = { ...payLoad, mentions };
}
let new_post = await new Post(payLoad).save();
return res.status(200).send(new_post);
} catch (error) {
return res.status(500).send(error.message);
}
};
When I pass plain-text in media attribute then It is saved to the database but when I pass image or video from postman form-data then the media is getting saved as an empty array
The API endpoint:
router.post("/createPost", auth, storage, post.createPost);
Can anyone assist me here?

Expressjs multer middleware save file in dynamic destination

I am using multer with expressjs and I am trying to change the DIR to save images depending on the request to a dynamic destination, my code is working fine but always save images inside the post directory, I'm using this middleware with multi requests.
1- How can I make the directory dynamic! example: to save to ./public/products if req product & save to ./public/posts if req post
2- How to make sure that the file is uploaded to the directory with no errors in the controller! then save the path to the database!
3- Is this the best practice to use multer ! in middleware level!
multer middleware fileUpload.js
const multer = require("multer");
const mkdirp = require("mkdirp");
const fs = require("fs");
const getDirImage = () => {
// const DIR = './public/uploads/products';
return `./public/posts`;
};
let storage = multer.diskStorage({
destination: (req, file, cb) => {
console.log(req.params,'&&&&&&&&&&&&',file);
let DIR = getDirImage();
if (!fs.existsSync(DIR)) {
fs.mkdirSync(DIR, { recursive: true });
}
cb(null, DIR);
},
filename: (req, file, cb) => {
const fileName = "overDress" + Date.now() + "" +
file.originalname.toLowerCase().split(' ').join('-');
cb(null, fileName)
},
});
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 10 //upto 2 megabytes per file.
},
fileFilter: (req, file, cb) => {
if (file.mimetype == "image/png" || file.mimetype == "image/jpg" ||
file.mimetype == "image/jpeg") {
cb(null, true);
} else {
cb(null, false);
return cb(new Error('File types allowed .jpeg, .jpg and .png!'));
}
}
});
module.exports = upload;
product.js route
const controller = require('../controllers/product.controller');
import { Router } from 'express'; //import from esm
import upload from '../middleware/fileUpload'
const router = Router();
router
.get('/', controller.list)
.post('/', upload.single('image'), controller.create)
.get('/:id', controller.getOne)
export default router;
and my create controller:
exports.create = async (req, res, next) => {
const { name, title, description,subtitle} = req.body;
if (!name || !title) {
return res.status(400).send({
message: 'Please provide a title and a name to create a product!',
});
}
try {
if (req.file) {
req.body.image = req.file.destination + '/' + req.file.filename;
}
const PRODUCT_MODEL = {
name: req.body.name,
title: req.body.title,
description: req.body.description,
image: req.body.image,
};
try {
const product = await Product.create(PRODUCT_MODEL);
console.log('product crerated');
return res.status(201).json(product);
} catch (error) {
console.log(error);
return res.status(500).send({
message: 'Something went wrong: ' + error,
});
}
} catch (error) {
return res.status(500).json(error);
}
};

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');
})

Express, NodeJs, Postgres, and Multer - Multiple fields upload - undefined Postman

I'm working on a rest API's PUT route, that updates fields in my postgres database with images or at least the image path to be stored there. While testing using postman, the fields keep returning undefined in my request body in postman's console. Is there a obvious reason that I don't see why files are not being received or picked up on by multer when selected for upload?
//DB Table
async function updateChannel({channelname, profile_avatar, slider_pic1, slider_pic2, slider_pic3}) {
try {
const userprofile = getUserChannelByChannelName(channelname);
await client.query(
`
UPDATE users_channel
SET profile_avatar=$2, slider_pic1=$3, slider_pic2=$4, slider_pic3=$5
WHERE channelname=$1
RETURNING *;
`,
[channelname, profile_avatar, slider_pic1, slider_pic2, slider_pic3]
);
return userprofile;
} catch (error) {
throw error;
}
}
//Multer Setup
const path = require("path");
const multer = require("multer");
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "./useruploads");
},
filename: (req, file, cb) => {
cb(null, Date.now() + "_" + path.extname(file.originalname));
},
});
const upload = multer({ storage: storage });
//API PUT ROUTE
usersRouter.put(
"/myprofile/update/:channelname",
upload.fields([
{ name: "avatar", maxCount: 1 },
{ name: "slide1", maxCount: 1 },
{ name: "slide2", maxCount: 1 },
{ name: "slide3", maxCount: 1 },
]),
requireUser,
async (req, res, next) => {
const { channelname } = req.params;
const { profile_avatar, slider_pic1, slider_pic2, slider_pic3 } = req.body;
try {
const updateData = {
profile_avatar: req.files.avatar,
slider_pic1: req.files.slide1,
slider_pic2: req.files.slide2,
slider_pic3: req.files.slide3,
};
console.log(updateData);
const channel = await getUserChannelByChannelName(channelname);
const updatedchannel = await updateChannel(channelname, updateData);
res.send({channel, updatedchannel});
} catch (error) {
console.error("Could not update user profile", error);
next(error);
}
}
);

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");
}
});

Resources