My post method is a part of an API that I am using to upload files and to store them in a folder in a local directory. When the user uploads the file it gets stored in the local directory and the path of the image is logged in the console. I am trying to render the path that is logged in the POST method to the EJS template that I am rendering in the get route. I am new to express and node is there any way I can do this? Here is my code:
const storage = multer.diskStorage({
destination: './upload/images',
filename: (req, file, cb) => {
return cb(null, `${file.fieldname}_${Date.now()}${path.extname(file.originalname)}`)
}
})
const upload = multer({
storage: storage,
limits:{
fileSize: 10485760
}
})
app.post("/upload", upload.single('profile'), (req, res) => {
res.redirect("/main")
let imgPath = req.file.path;
console.log(imgPath);
})
function errHandler(err, req, res, next) {
if (err instanceof multer.MulterError) {
res.json({
success: 0,
message: err.message
})
}
}
app.use(errHandler);
This logs "upload\images\profile_1609158104360.jpg"
My get function in which I am trying to access the loged path from the /upload post route
app.get("/main", function (req, res) {
if (req.isAuthenticated()) {
// res.render("main");
User.find({ "secret": {$ne: null}}, function(err, foundUser, imgPath){
if(err){
console.log(err);
}else{
if(foundUser){
res.render("main", {
usersWithSecrets: foundUser,
usersWithImage: imgPath
});
console.log(imgPath);
}
}
}
);
} else {
res.redirect("/login");
}
});
You cant. On post /upload you return redirect. Than browser makes get /main, which is totally different request.
Related
I have this router and I'm using multer to store files. I need to varify is 'Key' Exists in the server. Only after that server will store the file.
router.post('/', (req, res) => {
console.log(req.body.key); // empty here
upload(req, res, async (err) => {
if (err) {
console.log(err);
res.send({ error: err.message });
} else {
console.log(req.body.key); //shows the key
store(req.file.filename, {filename: req.file.filename, downloaded: 0, key: req.body.key});
res.send({ success: true, downlink: req.file.filename});
}
});
});
let storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, 'uploads/'),
filename: (req, file, cb) => {
const filename = `${uuid()}-${file.originalname}`;
cb(null, filename);
}
});
let upload = multer({
storage: storage,
limits: { fileSize: 15000000 },
}).single('file'); //name field name
How can I add a checkpoint to prevent Unauthorized file upload?
https://i.stack.imgur.com/gZAHE.png
I am trying to use multer module in my website but my doubt is that we are declaring upload as constant const upload =multer({}); but then in the post method, we are using upload (req,res,error) method is it the same object or a different one. Can someone please help me out it would be a great help.
const upload = multer({
storage: storage,
limits:{fileSize: 1000000},
fileFilter: function(req, file, cb){
checkFileType(file, cb);
}
}).single('myImage');`
`app.post('/upload', (req, res) => {
upload(req, res, (err) => {
if(err){
res.render('index', {
msg: err
});
} else {
if(req.file == undefined){
res.render('index', {
msg: 'Error: No File Selected!'
});
} else {
res.render('index', {
msg: 'File Uploaded!',
file: `uploads/${req.file.filename}`
});
}
}
});
});
The upload is defined as object but in the post method we are defining a method
I am using multer to uplaod images. I can upload them to my database with extension. Everything look perfect but I cannot see them on my webpage.
Also note that my project needs specific images for every different articles that means in every article images changes.
This is my article.js relevant codes:
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/uploads')
},
filename: function (req, file, cb) {
cb(null, file.originalname)
}
})
const upload = multer({storage: storage})
// Add Submit POST Route
router.post('/add',upload.single('img'), function(req, res){
req.checkBody('title','Title is required').notEmpty();
//req.checkBody('author','Author is required').notEmpty();
req.checkBody('body','Body is required').notEmpty();
// Get Errors
let errors = req.validationErrors();
if(errors){
res.render('add_article', {
title:'Add Article',
errors:errors
});
} else {
var article = new Article({
title: req.body.title,
author: req.user._id,
img: `uploads/${req.file.filename}`,
body: req.body.body,
});
article.save(function(err){
if(err){
console.log(err);
return;
} else {
res.render('add_article', {
});
}
});
}
});
And:
// Get Single Article
router.get('/:id', function(req, res){
Article.findById(req.params.id, function(err, article){
//User.findById(article.author, function(err, user){
res.render('article', {
article:article,
//imagePath: article.img,
//author: user.name
});
});
});
This is my article.pug:
extends layout
block content
br
//h5 Written by #{author}
h1= article.title
p= article.body
img.img-responsive(src=article.img)
Also I havent forgotten enctype or file upload stuffs in my add_article.pug page. But still I cannot understand why it is not uploading.
This is my console.log(article.img): uploads/image.jpeg
But still I cannot understand why I cannot see them. Every answer will be apreciated.
The following code works as expected to upload and retrieve files:
// Mongo URI
const mongoURI = 'mongodbconnstring';
// // Create mongo connection
const conn = mongoose.createConnection(mongoURI);
// // Init gfs
let gfs;
conn.once('open', () => {
gfs = new mongoose.mongo.GridFSBucket(conn.db, {
bucketName: 'Uploads'
});
});
// Create storage engine
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
const filename = file.originalname; //+ Date.now();
const fileInfo = {
filename: filename,
bucketName: 'Uploads'
};
resolve(fileInfo);
});
}
});
const upload = multer({ storage });
// // #route POST /upload
// // #desc Uploads file to DB
routerUpload.post('/upload', upload.single('files'), (req, res) => {
//res.json({file: req.file})
res.redirect('/');
});
// #route GET /files
// #desc Display all files in json
routerUpload.get('/files', (req, res) => {
gfs.find().toArray((err, files) => {
// Check if files
if (!files || files.length === 0) {
return res.status(404).json({
err: 'No files exist'
});
}
return res.json(files);
});
});
// #route GET /files/:filename
// #desc Display single file object
routerUpload.get('/files/:filename', (req, res) => {
const file = gfs
.find({
filename: req.params.filename
})
.toArray((err, files) => {
if (!files || files.length === 0) {
return res.status(404).json({
err: 'No file exists'
});
}
gfs.openDownloadStreamByName(req.params.filename).pipe(res);
});
});
However, when I call the code below to delete the file by ID:
// #route DELETE /files/:id
// #desc Delete file
routerUpload.post('/files/del/:id', (req, res) => {
gfs.delete(req.params.id, (err, data) => {
if (err) {
return res.status(404).json({ err: err.message });
}
res.redirect('/');
});
});
I get this error in Postman:
{
"err": "FileNotFound: no file with id 5db097dae62a27455c3ab743 found"
}
Can anyone point me in the direction of where I need to go with this? I tried with the delete method instead of post, and even specified the root of the file, but the same error occurs. Thanks!
Have you tried this
// #route DELETE /files/:id
// #desc Delete file
routerUpload.post('/files/del/:id', (req, res) => {
gfs.remove({ _id: req.params.id, root: "uploads" }, (err, gridStore) => {
if (err) {
return res.status(404).json({ err });
}
return;
});
});
However I'm using an old version of express hence receiving this
(node:6024) DeprecationWarning: collection.remove is deprecated. Use deleteOne, deleteMany, or bulkWrite instead.
and
(node:6024) DeprecationWarning: Mongoose: findOneAndUpdate() and findOneAndDelete() without the useFindAndModify option set to false are deprecated
this is due to the version of express, multer and GridFs that I'm using
express: ^4.17.1
multer: ^1.4.2"
multer-gridfs-storage: ^3.3.0
I’m receiving a 404 Not Found and "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client" when I attempt to submit a form that has two images uploaded. Neither image is a required field, so when I upload one image (can be either) everything works correctly, and I do not get an error. The issue only occurs when I upload both in one request. Checking my uploads folder, when I get a 404, both images are correctly uploaded.
Here is the code for multer:
const multerOptions = {
storage: multer.memoryStorage(),
fileFilter(req, file, next) {
const isPhoto = file.mimetype.startsWith('image/');
if (isPhoto) {
next(null, true);
} else {
next({ message: 'That filetype isn\'t allowed!' }, false);
}
},
};
export const upload = multer(multerOptions).fields([
{ name: 'avatar' },
{ name: 'accolade' },
]);
export const resize = async (req, res, next) => {
if (!req.files) {
next();
return;
}
Object.keys(req.files).forEach(async (file) => {
const extension = req.files[file][0].mimetype.split('/')[1];
req.body[file] = `${uuid.v4()}.${extension}`;
const uploaded = await jimp.read(req.files[file][0].buffer);
if (file === 'avatar') {
await uploaded.resize(300, jimp.AUTO);
} else if (file === 'accolade') {
await uploaded.resize(30, jimp.AUTO);
}
await uploaded.write(`./public/uploads/${req.body[file]}`);
next();
});
};
Here is the route:
router.post(
'/team-members/add/:id',
authController.authCheck,
userController.isAdmin,
userController.upload,
userController.resize,
userController.validateUser,
catchErrors(userController.addTeamMember),
);
And here are the other middleware methods in the route:
export const authCheck = (req, res, next) => {
(req.isAuthenticated()) ? next() : res.redirect('/login');
};
export const isAdmin = (req, res, next) => {
(req.user.role !== 'admin') ? res.redirect('/dashboard') : next();
};
export const validateUser = (req, res, next) => {
req.checkBody('firstName', 'There must be a first name!').notEmpty();
req.checkBody('lastName', 'There must be a last name!').notEmpty();
req.checkBody('email', 'There must be an email!').notEmpty();
req.checkBody('role', 'A role must be specified!').notEmpty();
const errors = req.validationErrors();
if (errors) {
req.flash('error', errors.map(err => err.msg));
res.redirect('back');
}
next();
};
And finally the function to add a user (it's wrapped in a function that catches errors rather than catching errors in the controller):
export const addTeamMember = async (req, res) => {
const org = await Org.findOne({ _id: req.params.id });
if (org) {
const newUser = new User(req.body);
newUser.organization = org._id;
newUser.invitation = true;
await newUser.save();
await org.update({ $push: { users: newUser } });
const inviteLink = `http://${req.headers.host}/join/${org._id}`;
await send({
user: newUser,
filename: 'invitation',
subject: `Welcome ${newUser.email}`,
inviteLink,
});
req.flash('success', `Yay! An invitation has been sent to ${newUser.email}`);
res.redirect(`/team-members/${org._id}`);
} else {
req.flash('error', 'No organization found!');
req.redirect('back');
}
};
I only get the error when I upload both an avatar and an accolade in one request. If I upload just one in a single request, I get no errors. In both cases, the image(s) are uploaded to the uploads directory I've specified, the user is added to my db, and an email for an invite is fired off. The redirect on success is a single GET request to a view with the same authCheck and isAdmin middlewares.
I've gone through and commented out the portions of code that are not necessary to submit the request (checkAuth, isAdmin, validateUser, and sending the email) but as long as I upload two in one request I get an error. Any ideas where I'm going wrong?
Posting the answer in case this ever trips anyone else up.
The next() call is inside the forEach block in the resize method, thus it is being called for each file that is being uploaded. Moving it outside the block (obviously) fixed the issue.