In my project the admins have the ability to upload MP3 files and submit parameters to it like song name.
I decided using multer middleware for handling multipart/form-data.
My problem is req.body.gender returns always undefined, because I have to use it inside the uploadSong listener. I want to upload the song when the gender is zero.
index.ejs
<form method="post" action="/acp" role="publish" enctype="multipart/form-data">
<div class="form-group">
<input type="file" name="song" id="song" accept="audio/mpeg">
</div>
<input type="checkbox" name="gender" checked data-toggle="toggle" data-on="Male" data-off="Female">
</form>
app.js
var uploadSong = upload.single('song');
app.post('/acp', isLoggedIn, function (req, res) {
console.log(req.body.gender); // returns "undefined"
if(req.body.gender == 0) { // returns "undefined"
uploadSong(req, res, function (err) {
if (err) {
res.send('uploaded');
return;
}
res.redirect('/');
});
}
});
(A) Not possible with multer.
(B) Use busboy. It uses streams to parse the form data and so you can get form elements values before the file upload and the fields are made available as events.
(C) Another solution (if you prefer using multer) is to use multer but add a header to send the value of the parameter to check before file upload. Headers are available as soon as the request reaches the server.
by using multer form-data parser you can parse form and access req.body before multer starts just register this app middle-ware:
import * as multer from "multer";
// parse form-data
app.use(multer().any());
This is my sample code, it is woking fine, if you need further explanation please let me know. hope helpful.
var Hotel = require('../models/hotel');
var path = require('path');
var multer = require('multer');
var uplodedImages = [];
var storageHotelGallery = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads/hotelGallery');
},
filename: function (req, file, cb) {
console.log(req.body);
var newFileName = Date.now() + path.extname(file.originalname);
req.newFileName = newFileName;
cb(null, newFileName);
uplodedImages.push(newFileName);
}
});
var uploadHotelGallery = multer({ storage: storageHotelGallery}).fields([{ name: 'imgArr', maxCount: 8 }]);
module.exports = function(router) {
// ADD HOTEL BASIC DATA TO CREATE HOTEL OBJECT
router.post('/createHotelStep1', function(req, res) {
if( req.body.name == null || req.body.name == '' ) {
res.json({ success: false, message: "Hotel name required" });
res.end();
}
else if( req.body.addressline1 == null || req.body.addressline1 == '' ) {
res.json({ success: false, message: "Address line 1 is required" });
res.end();
}
else if( req.body.city == null || req.body.city == '') {
res.json({ success: false, message: "City is required" });
res.end();
}
else {
var hotel = new Hotel();
hotel.name = req.body.name;
hotel.addressline1 = req.body.addressline1;
hotel.addressline2 = req.body.addressline2;
hotel.phone = req.body.phone;
hotel.city = req.body.city;
hotel.email = req.body.email;
hotel.save(function(err) {
if (err) {
res.json({ success: false, message: "Unable to Complete Hotel Step 1" });
} else {
res.json({ success: true, message: 'Create Hotel Step 1 Complete', _id : hotel._id });
}
});
}
});
router.post('/createHotelGallery', function (req, res, next) {
uplodedImages = [];
uploadHotelGallery(req, res, function(err) {
if(err) {
res.json({ success: false, message: 'Could not upload images'});
res.end();
}
else {
Hotel.findOne({ _id:req.body._id }).populate('users').exec(function (err, hotel) {
if (err) {
res.json({ success: false, message: 'Could not save uploaded images to database'});
res.end();
}
else {
for(var x=0; x<uplodedImages.length; x++)
hotel.images.push(uplodedImages[x]);
hotel.save();
res.json({ success: true, message: 'Gallery image uploaded' });
res.end();
}
});
}
});
});
return router;
}
This is my sample code, it is woking fine
const upload = multer({
storage,
fileFilter(req, file, cb) {
if(req.body.name===''){
return cb(new Error('Invalid name'), false)
}
const extname = path.extname(file.originalname).toLowerCase() === '.gz'
const mimetype = file.mimetype === 'application/x-gzip'
if (mimetype && extname) {
return cb(null, true)
} else {
return cb(new Error('Invalid mime type'), false)
}
},
})
Related
I'm developing this server based on this tutorial, https://www.bezkoder.com/angular-12-node-js-express-mysql/ now I'm in need of add some file upload functionalities, using Multer. but each time I try to us it, I get this error:
/home/miguel/Documents/angular_projs/examples/server/node_modules/multer/lib/make-middleware.js:45
next(err)
^
TypeError: next is not a function
at done (/home/miguel/Documents/angular_projs/examples/server/node_modules/multer/lib/make-middleware.js:45:7)
when i use it here:
//File upload configuration
const maxSize = 2 * 1024 * 1024;
let storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "/resources/static/assets/uploads/");
},
filename: (req, file, cb) => {
console.log(file.originalname);
cb(null, file.originalname);
},
});
let uploadFile = multer({
storage: storage,
limits: { fileSize: maxSize },
}).single("file");
/* ---------------------------------------------------------------------------------------------------- */
//Upload Files
exports.file_upload = async (req, res) => {
let number = req.params.number;
try {
await uploadFile(req, res);
if (req.file == undefined) {
return res.status(400).send({ message: "Please upload a file!" });
}
res.status(200).send({
message: "Uploaded the file successfully: " + req.file.originalname,
});
} catch (err) {
if (err.code == "LIMIT_FILE_SIZE") {
return res.status(500).send({
message: "File size cannot be larger than 2MB!",
});
}
res.status(500).send({
message: `Could not upload the file: ${req.file.originalname}. ${err}`,
});
}
//Check if empty
//res.status(200).json({msg:`${s03}`});
};
I've checked other tutorial and it's suppose to work
Try passing in next as a parameter.
...
exports.file_upload = async (req, res, next) => {
let number = req.params.number;
try {
await uploadFile(req, res, next);
...
I have a problem with express.js and multer when I try to upload 2 valid images and 1 example pdf to validate is all images, it will upload that two images into a folder, and then it will throw the error for pdf that is an invalid format, can I somehow validate first all images and then do the upload to folder or throw the error is something is wrong here is my code
const fileStorageEngine = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './images');
},
filename: (req, file, cb) => {
cb(null, Date.now()+ '--' +file.originalname);
}
});
const fileFilter = (req, file, cb) => {
// Reject a file
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/jpg' || file.mimetype === 'image/png') {
cb(null, true);
} else {
req.fileValidationError = 'File type not supported';
cb(null, false);
}
};
const upload = multer({
storage: fileStorageEngine,
limits: {
fileSize: 1024 * 1024 * 5 // Accept files to 5mb only
},
fileFilter: fileFilter
});
app.post('/multiple', upload.array('images', 3), async(req, res, next) => {
try {
console.log("POST Multiple Files: ", req.files);
if (await req.fileValidationError) {
throw new Error(req.fileValidationError);
} else {
for (let i = 0; i < req.files.length; i++) {
let storeImage = await StoreImages.create({
images: req.files[i].path
});
if (!storeImage) {
throw new Error('Sorry, something went wrong while trying to upload the image!');
}
}
res.status = 200;
res.render("index", {
success: true,
message: "Your images successfully stored!"
});
}
} catch(err) {
console.log("POST Multiple Error: ", err);
res.status = 406;
return res.render('index', {
error: true,
message: err.message
})
}
});
I want to validate all uploaded files before insert to a folder, server, etc...
I found a solution by throwing the error in cb function in fileFilter function
const fileFilter = (req, file, cb) => {
// Reject a file
if(file.mimetype === 'image/jpeg' || file.mimetype === 'image/jpg' || file.mimetype === 'image/png'){
cb(null, true);
}else{
cb(new Error('File type not supported'));
}
};
For error handling multer suggest
const multer = require('multer')
const upload = multer().single('avatar')
app.post('/profile', function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
} else if (err) {
// An unknown error occurred when uploading.
}
// Everything went fine.
})
})
I wrote a custom middleware for uploading different types of file.
const { check } = require("express-validator")
const multer = require('multer')
const mime = require('mime-types')
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
const filename = Date.now()+"-"+file.originalname
cb(null, filename)
}
})
const upload = multer({ storage: storage })
const uploadFile = (fieldname,filetypes,fileSize)=>{
return (req,res,next)=>{
let file = upload.single(fieldname)
file(req,res,function(err){
if (err instanceof multer.MulterError) {
req.fileError = {
param: "image",
msg: "Unable to process request"
}
return next()
} else if (filetypes.includes(mime.extension(req.file.mimetype)) === false) {
req.fileError = {
param: "image",
msg: `Only ${filetypes.toString()} allowed`
}
return next()
} else if (req.file.size > fileSize) {
req.fileError = {
param: "image",
msg: `File size should not exceed ${formatBytes(req.file.size)}`
}
return next()
}
})
}
}
course_validator = [
check("name")
.trim()
.isLength({min:3,max:100})
.withMessage("Course name should be between 3 to 100 characters")
]
app.get("/create/post",uploadFile("image",["jpeg","jpg"],122880),(req,res)=>{
const errors = validationResult(req)
if(!errors.isEmpty()){
return res.json({
status: false,
error: req.fileError ? [...errors.array(),req.fileError] : errors.array()
})
}
})
If there is no error then only I need to upload the file to uploads folder. When I upload a other than jpeg or jpg I am getting error with message that Only jpeg,jpg allowed. This is what I need. But the problem is the file is also getting uploaded to uploads folder.
For custom error messages you can go through this controller here I'm checking to file type when uploading an image and in the controller, if there is no file selected at that time I'm sending a custom message with simple if the condition after passing all if image and the products will be saved in DB
exports.postProduct = (req, res, next) => {
const title = req.body.title;
const image = req.file;
const price = req.body.price;
const description = req.body.description;
if (!image) {
return res.status(422).render("admin/add-product", {
pageTitle: "Add Product",
path: "/adminproducts",
hasError: true,
product: {
title: title,
price: price,
description: description,
},
errorMessage: "Atteched file is not an image!!!",
validationErrors: [],
});
}
const imageUrl = image.path;
const product = new Product({
title: title,
imageUrl: imageUrl,
price: price,
description: description,
userId: req.user,
});
product
.save()
.then((results) => {
console.log("Product Created Successfully");
res.redirect("/admin/products");
})
.catch((err) => {
console.log(err);
});
};
I'm trying to open places.ejs file by clicking the submit button on show.js page, just like the show.ejs page opens on clicking the submit button on new.ejs file, but a reference error is occurring. Please help me fix the error. I'm attaching herewith my routes.js code and a part of my index.js code Any help would be highly appreciable. Thank you
Here's my routes.js code
const { con, sessionStore } = require('./config/db');
exports.new = function (req, res) {
message = '';
if (req.method == "POST") {
const post = req.body;
const username = post.username;
const title = post.title;
const state = post.state;
const category = post.category;
const description = post.description;
if (!req.files)
return res.status(400).send('No files were uploaded.');
const file = req.files.uploaded_image;
var img_name = file.name;
if (file.mimetype == "image/jpeg" || file.mimetype == "image/png" || file.mimetype == "image/gif") {
file.mv('public/imgs/uploads/' + file.name, function (err) {
var sql = "INSERT INTO `nt_data`(`username`,`title`,`state`,`category`, `images` ,`description`) VALUES (?,?,?,?,?,?)";
var query = con.query(sql, [username, title, state, category, img_name, description], function (err) {
console.log(err)
if (!err) {
res.redirect('show/' + username + '/' + category);
}
else {
message = "This format is not allowed , please upload file with '.png','.gif','.jpg'";
res.render('new.ejs', { message: message });
}
});
});
}
}
else {
res.render('new');
}
};
exports.show = function (req, res) {
let message = '';
con.query('SELECT * FROM nt_data WHERE username=? AND category=?', [req.params.username, req.params.category], (err, result) => {
console.log(err)
if (result.length <= 0) {
message = "show not found!";
res.render('show.ejs', { data: result, message: message });
}
else {
res.redirect('places/' + username);
}
});
res.render('show');
};
here's a part of my index.js code
app.get('/new', loginRequired, routes.new);
app.post('/', loginRequired, routes.new);
app.get('/show/:username/:category', loginRequired, routes.show);
app.post('/', loginRequired, routes.show);
app.get('/places/:username', loginRequired, routes.show);
error
ReferenceError: data is not defined
ReferenceError: username is not defined
In show function, you need to get username like this:
req.params.username
And for data I don't see where do you reference it, in witch line to do get an error?
exports.show = function (req, res) {
let message = '';
con.query('SELECT * FROM nt_data WHERE username=? AND category=?', [req.params.username, req.params.category], (err, result) => {
console.log(err)
if (result.length <= 0) {
message = "show not found!";
res.render('show.ejs', { data: result, message: message });
}
else {
res.redirect('places/' + req.params.username); // Change here
}
});
res.render('show');
};
I'm trying to upload a picture where the user registers in the app. When I'm trying to test it in insomnia I get the following error: MulterError: Unexpected field at wrappedFileFilter
I've been reading online and I know this error occurs when there is a mismatch between the field name provided by the client and the field name expected by the server. However, I've been checking many times my code and the name that I give in the server and the client (Insomnia) is the same: "image". Any idea what else can I try?
Here is my code:
const storage = multer.diskStorage({
destination: './client/public/img',
filename: (req, file, cb) => {
console.log(file)
cb(null, file.originalname)
}
})
const fileFilter = (req, file, cb) => {
if(file.mimetype == "image/jpeg" || file.mimetype == "image/png") {
cb(null, true)
} else {
cb(null, false)
}
}
const upload = multer({ storage: storage, fileFilter: fileFilter, destination: './client/public/img' })
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
//sign up
routes.post("/register", upload.single("image"), (req, res) => {
let { user_name, email, password, password2 } = req.body;
let { image } = req.file.filename
let errors = []
//check required inputs
if(!user_name || !email || !password || !password2){
errors.push({message: "Please fill all required fields"})
res.send({message: "Please fill all required fields"})
}
//check passwords
if(password != password2) {
errors.push({message: "Passwords do not match"})
res.send({message: "Passwords do not match"})
}
if(errors.length>0) {
console.log(errors);
} else {
if(email) {
db(`SELECT * FROM user WHERE email = "${email}"`)
.then((results) => {
if(results.data.length>0){
res.send("Email exists")
} else {
bcrypt.hash(password, saltRounds, (err, hash) => {
password = hash
db(`INSERT INTO user (user_name, email, password, image) VALUES ("${user_name}", "${email}", "${password}", "${image}")`)
.then((results) => {
res.send("Registration successful")
if(err)throw err;
})
.catch((error) => {
console.log(error)
})
})
}
})
.catch((error) => {
console.log(error)
})
} else {
res.send("Enter email")
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Any help will be appreciated. Thanks in advance.
I am making the assumption that you are using form to upload your file. If, so...
Front end snippet:
<form action="/register" method="post" enctype="multipart/form-data">
<input type="file" name="image" />
</form>
Get multer to upload the file correctly:
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './client/public/img')
},
filename: function (req, file, cb) {
if(file.mimetype == "image/jpeg" || file.mimetype == "image/png") {
cb(null, file.fieldname + '-' + Date.now());
}
else {
cb(new MulterError('LIMIT_UNEXPECTED_FILE', file.fieldname));
}
});
const upload = multer({ storage: storage });
Handle the error inside your Express middleware by calling upload as below...
app.post('/profile', function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
} else if (err) {
// An unknown error occurred when uploading.
}
// Everything went fine.
})
})