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);
}
}
);
Related
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)
}
})
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?
I'm trying to send FormData to nodeJs. I appended the values 'id' and 'photo' to the form data. In the front-end, I can clearly see that the FormData is there in the browser's console log, but it is undefined in the nodeJs backend
the backend:
const random = Math.floor(Math.random() * 100000) + 10000;
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './client/public/images');
},
filename: function (req, file, cb) {
cb(null, random.toString() + new Date().getTime() + '.jpg');
},
});
const move = multer({ storage: storage });
const upload = move.single('photo');
router.get('/upload-photo', upload, (req, res) => {
const id = req.body.id;
const photo = req.files['photo'][0].filename;
db.query(
'UPDATE users SET photo = ? WHERE id = ?',
[photo, id],
function (err, rows, fields) {
if (err) throw err;
if (rows.length >= 1) {
rows.map((entry) => {
const user = {};
COLUMNS.forEach((c) => {
user[c] = entry[c];
});
const theUser= {
id: user.id,
photo: user.photo,
};
res.json(theUser);
});
} else {
return res.status(200).json({});
}
}
);
});
The function :
function photoUpload() {
const photoData = new FormData();
photoData.append('id', id);
photoData.append('photo', photoFile);
dispatch(uploadPhoto(photoData));
}
the uploadPhoto action:
export const uploadPhoto = (photoData) => (dispatch) => {
axios
.get(`/api/user/upload-photo`, photoData)
.then((res) => {
dispatch(getPhotos());
})
.catch((err) => {
let message = typeof err.response != 'undefined' ? err.response.data : {};
dispatch({
type: GET_ERRORS,
payload: message,
});
});
};
I don't know if it matters or not, but there is another route like this in a different api file and it works fine. This is basically the same code as that one with the only difference being that the other route uploads multiple files along with multiple req.body data. And that one works perfectly
Instead of:
const photo = req.files['photo'][0].filename;
I had to do:
const photo = req.file.filename; since it was just only one file
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);
}
};
I am trying to upload a product using postman and anytime I submit; it sends back all the data with the image undefined as shown in this screenshot:
My controller file:
const gameRepository = require("../routes/repository")
exports.createGame = async (req, res, next) => {
try {
const PORT = 8000;
const hostname = req.hostname;
const url = req.protocol + '://' + hostname + ':' + PORT + req.path;
const payload = ({
name: req.body.name,
price: req.body.price,
category: req.body.category,
gameIsNew: req.body.gameIsNew,
topPrice: req.body.topPrice,
isVerOrient: req.body.IsVerOrient,
description: req.body.description,
image: url + '/imgs/' + req.path
});
let eachGame = await gameRepository.createGame({
...payload
});
console.log(req.body)
res.status(200).json({
status: true,
data: eachGame,
})
} catch (err) {
console.log(err)
res.status(500).json({
error: err,
status: false,
})
}
}
repository.js:
const Game = require("../models/gameModel");
exports.games = async () => {
const games = await Game.find();
return games;
}
exports.gameById = async id => {
const game = await Game.findById(id);
return game;
}
exports.createGame = async payload => {
const newGame = await Game.create(payload);
return newGame;
}
exports.removeGame = async id => {
const game = await Game.findById(id);
return game;
}
Multer.js:
const multer = require("multer");
const path = require("path");
// checking for file type
const MIME_TYPES = {
'imgs/jpg': 'jpg',
'imgs/jpeg': 'jpeg',
'imgs/png': 'png'
}
// Image Upload
const storage = multer.diskStorage({
destination: (req, file, cb ) => {
cb(null, path.join('../imgs'));
},
filename: (req, file, cb) => {
const name = file.originalname.split('').join(__);
const extension = MIME_TYPES[file.mimetype];
cb(null, name + new Date().toISOString() + '.' + extension);
}
});
module.exports = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 6
},
})
I am not sure about where I went wrong, that is why I need an external eye to help locate where the fault is coming from.
I have a feeling that I need to use body-parser or navigate into the image folder correctly, or multi-part form, I am not sure.
after many try and fail I finally figured it out.
turns out it has compatibility issues depending on your OS.
I use windows 10 and this resolved the issue for me
Here is my working code:
multer.js
const multer = require("multer");
const path = require("path");
// checking for file type
const MIME_TYPES = {
'image/jpg': 'jpg',
'image/jpeg': 'jpeg',
'image/png': 'png'
}
// Image Upload
const storage = multer.diskStorage({
destination: (req, file, cb ) => {
cb(null, ('storage/imgs/'));
},
filename: (req, file, cb) => {
const extension = MIME_TYPES[file.mimetype];
// I added the colons in the date of my image with the hyphen
cb(null, `${new Date().toISOString().replace(/:/g,'-')}.${extension}`);
}
});
module.exports = multer({
storage: storage
})
In my controller.js
const gameRepository = require("../routes/repository");
exports.createGame = async (req, res, next) => {
try {
const payload = {
name: req.body.name,
price: req.body.price,
category: req.body.category,
gameIsNew: req.body.gameIsNew,
topPrice: req.body.topPrice,
isVerOrient: req.body.IsVerOrient,
description: req.body.description,
image: req.file.filename,
};
let eachGame = await gameRepository.createGame({
...payload,
});
res.status(200).json({
status: true,
data: eachGame,
});
} catch (err) {
console.log(err);
res.status(500).json({
error: err,
status: false,
});
}
};
exports.getGames = async (req, res) => {
try {
let games = await gameRepository.games();
res.status(200).json({
status: true,
data: games,
});
} catch (err) {
console.log(err);
res.status(500).json({
error: err,
status: false,
});
}
};
exports.getGameById = async (req, res) => {
try {
let id = req.params.id;
let gameDetails = await gameRepository.gameById(id);
req.req.status(200).json({
status: true,
data: gameDetails,
});
} catch (err) {
res.status(500).json({
status: false,
error: err,
});
}
};
exports.removeGame = async (req, res) => {
try {
let id = req.params.id;
let gameDetails = await gameRepository.removeGame(id);
res.status(200).json({
status: true,
data: gameDetails,
});
} catch (err) {
res.status(500).json({
status: false,
data: err,
});
}
};
:
Postman output
Thanks to this great community.