Uploading file with multer AND get data from ajax - node.js

I have a problem to using multer when i have some data to pass with ajax at the same time (from the front side) to the server side (with nodejs).
I think there is a conflict between the two methods. Here an example :
My HTML :
<form action="api/stats" enctype="multipart/form-data" method="post">
<div class="form-group">
<div class="mb-3">
<label for="inputBookName" class="form-label">Name</label>
<input type="text" class="form-control" id="inputBookName">
</div>
<div class="mb-3">
<label for="inputBookCategory" class="form-label">Categories</label>
<select id="inputBookCategory" class="form-select" multiple="multiple"></select>
</div>
<input type="file" name="uploaded_file">
<input type="submit" value="Send!">
</div>
</form>
My client side : (I have to pass "bookInfo" object with some data that I get from select2 (categoryName). When i click on the submit button :
const dataSelectedCatagories = $('#inputBookCategory').select2('data');
const categories = [];
let bookInfo;
for (let j = 0; j < dataSelectedCatagories.length; j++) {
const category = dataSelectedCatagories[j];
const categoryId = category._resultId;
const categoryName = category.text;
categories.push({
id: categoryId,
name: categoryName
});
}
bookInfo = {
bookid: Date.now(),
bookname: bookName,
name: categoryName
};
$.ajax({
type:"POST",
dataType:"json",
contentType: "application/json",
data:JSON.stringify(bookInfo), //Send some datas to front
url:"api/stats"
}).done(function(response){
console.log("Response of update: ",response)
}).fail(function(xhr, textStatus, errorThrown){
console.log("ERROR: ",xhr.responseText)
return xhr.responseText;
});
My Server side
const multer = require('multer');
const path = require('path');
const image_storage = multer.diskStorage({
destination: path.join(__dirname , '../../public/uploads'),
filename: function(req, file, cb){
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname))
}
});
const fileUpload = multer({
storage: image_storage,
limits:{fileSize: 2000000} // 1 mb
}).single('uploaded_file');
uploadBookMulter = async (req, res) => {
console.log('body' , req.body) //get the datas from the front
await fileUpload(req, res, (err) => {
console.log('file : ', req.file) //here the req.file will turn out empty
})
};
The upload works very well. As well as the data feedback in ajax. The problem is when I plug the two together, the req.file is empty...
Isn't there a solution to have the data of the ajax and that of the multer side?

Related

File Fail To Upload

Started learning nodejs recently, and am currently trying to perform a task that involves file upload.. With the aid of multer documentation and Youtube video, was able to set up the module and middleware and file path etc. but for some reasons, the file don't upload. i can see the file object in my console, but my browser keeps loading.
Would love if someone can help point my errors to allow the file upload to specifies folder, and also give me a tip on how to trap errors not rendered on the error file Thank you. Below are my codes:
register.hbs
{{>header}}
<body>
<h1>Hello World</h1>
<h2>{{this.title}}</h2>
<div class="mb-3">
{{#each data}}
<div class="alert alert-danger">{{msg}}</div>
{{/each}}
</div>
<form action="" method="POST" enctype="multipart/form-data">
<label for="exampleInputEmail1" class="form-label">First Name</label>
<input type="text" class="form-control" name="fname" aria-describedby="emailHelp">
<div>
</div>
</div>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Last Name</label>
<input type="text" class="form-control" name="lname" aria-describedby="emailHelp">
</div>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Email address</label>
<input type="email" class="form-control" name="email" aria-describedby="emailHelp">
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Password</label>
<input type="password" class="form-control" name="pwd">
</div>
<div class="mb-3">
<label for="exampleInputFile" class="form-label">Select Image</label>
<input type="file" class="form-control" name="image" id="image">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">Check me out</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</body>
</html>
router file
var express = require('express');
var router = express.Router();
const {check,validationResult}= require('express-validator');
const multer=require('multer')
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/uploads')
},
filename: function (req, file, cb) {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)
cb(null, file.fieldname + '-' + uniqueSuffix)
}
})
var upload = multer({
storage: storage,
limits:{
fieldSize:1024*1024*100
} })
router.post('/register',upload.single('image'), [
check('fname')
.isLength({ min: 8 })
.withMessage('First name must be minimum of 8 characters')
.isAlpha()
.withMessage('first name must contain only alphabeth'),
check('lname').isLength({min:8}),
check('email').isEmail()
], (req, res,next)=>{
const errors = validationResult(req)
if (!errors.isEmpty()) {
//const data=errors.array();
console.log(errors)
res.render('users/register',{title:"Create Account", data:errors.array()});
//return res.status(422).json({errors: errors.array()})
}
console.log(req.file)
const fname = req.body.fname
const lname = req.body.lname
const email = req.body.email
console.log(req.errors)
// res.render('users/register');
})
Not really sure about your case, in my case I using multer like this:
I seperate multer to a file multerUploaderMiddleware.js
const multer = require("multer");
const path = require("path");
const util = require("util");
//Change this to diskStorage like your code above
//I use memory storage
const multerStorage = multer.memoryStorage()
function chapterIsExisted(req) {
// My custom error handling function
return (
Chapter
.findOne({ comicSlug: req.params.slug, chapter: `chapter-${req.body.chapter}` })
.then(chapterExisted => {
if (chapterExisted == null) { return false }
return true
}))
};
// This filter function for error handling
const filter = async (req, file, cb) => {
//as I remember if you want the file to success
// then return cb(null, true)
// else return cb(null, false)
if (req.check == 'nocheck') {
return cb(null, true)
} else {
var check = await chapterIsExisted(req)
if (check == false) {
cb(null, true)
} else {
let errorMess = `chapter ${req.body.chapter} existed`
return cb(errorMess, false);
}
}
};
const upload = multer({
storage: multerStorage,
fileFilter: filter
}).single("image", 200);
// Turn this middleware function into promise
let multipleUploadMiddleware = util.promisify(upload);
module.exports = multipleUploadMiddleware;
And In my route controller:
// get the exported function
const MulterUploadMiddleware =
require("../middlewares/MulterUploadMiddleWare");
class S3UploadController {
// [POST] / stored /comics /:slug /S3-multiple-upload
multipleUpload = async (req, res, next) => {
// The function you exported from middleware above
MulterUploadMiddleware(req, res)
.then(async () => {
//when the upload is done you get some data back here
Console.log(req.file)
// do stuff
})
.then(() => res.redirect('back'))
.catch(err => next(err))
})
}

file upload with axioso is showing undefined in multer

I am trying to upload file using react and axios to node backend using multer.
when i use form as :
<form action = '/article/add' method="POST" className = {adminStyle.add_article_form} enctype="multipart/form-data">
<input type='text' className={adminStyle.add_article_input_title} autoComplete = 'off' name='title' placeholder='Title' value = {state.title} onChange={changeHandler}/>
<select name='category' value = {state.category} onChange={changeHandler}>
{options}
</select>
<input type="file" name='thumbnail' className={adminStyle.add_article_input_file} onChange={changeFileHandler}/>
<textarea type = 'text' name='body' className={adminStyle.add_article_textarea} rows='30' cols='100' value = {state.body} onChange={changeHandler}></textarea>
<button type='submit'>Submit</button>
</form>
it works fine but when using axios the file is undefined:
I used axios as:
const [state,setState] = useState({
title: '',
category: 'Choose one',
body: '',
thumbnail: ''
})
const changeFileHandler = (e) => {
setState({ thumbnail: e.target.files[0] })
}
const postArticle = (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('thumbnail', state.thumbnail)
axios.post('/article/add', state,config).then(data => console.log(data))
}
<form onSubmit={postArticle} className = {adminStyle.add_article_form} enctype="multipart/form-data">
<input type='text' className={adminStyle.add_article_input_title} autoComplete = 'off' name='title' placeholder='Title' value = {state.title} onChange={changeHandler}/>
<select name='category' value = {state.category} onChange={changeHandler}>
{options}
</select>
<input type="file" name='thumbnail' className={adminStyle.add_article_input_file} onChange={changeFileHandler}/>
<textarea type = 'text' name='body' className={adminStyle.add_article_textarea} rows='30' cols='100' value = {state.body} onChange={changeHandler}></textarea>
<button type='submit'>Submit</button>
</form>
My backend is as follow:
const storage = multer.diskStorage({
destination: path.join(__dirname, '..', '../public/uploads'),
filename: function(req, file, cb){
cb(null,file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
function checkFileType(file, cb){
// Allowed extension name
const filetypes = /jpeg|jpg|png|gif/;
// Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
} else {
cb('Error: Images Only!');
}
}
const upload = multer({
storage: storage,
limits:{fileSize: 1000000},
fileFilter: function(req, file, cb){
checkFileType(file, cb);
}
}).single('thumbnail')
router.post('/add',(req, res)=>{
upload(req,res, (err) => {
console.log(req.file)
})
});
can anyone help me with this? I am really stuck. I need to upload the image in the backend.
You are passing state and not formData as the body of the request. You should change this line:
axios.post('/article/add', state,config).then(data => console.log(data))
with this line:
axios.post('/article/add', formData ,config).then(data => console.log(data))

how to upload single file with multiple form inputs using multer in express js?

I have different form inputs to upload in a single page on how to upload using multer?
app.js :
const multer = require('multer');
const fileStroge = multer.diskStorage({
destination: (req, file,cb)=>{
cb(null, 'upload');
},
filename: (req, file, cb) =>{
cb(null, file.originalname);
}
});
const fileFilter = (req, file, cb)=>{
if(file.mimetype == 'application/octet-stream'){
cb(null,true);
}else{
cb(null,false);
}
};
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extented: true}));
app.use(multer({ storage: fileStroge, fileFilter: fileFilter}).single('filename'));
// app.use(multer({ storage: fileStroge, fileFilter: fileFilter}).single('filenameS'));
below is my js file
user.js:
exports.uploadedData = (req, res)=>{
const file = req.file;
// console.log(file);
if( file == undefined){
res.send("you can upload excel sheet only");
}
readXlsxFile(req.file.path).then((rows) =>{
var database = mysql.createConnection(
{ host: 'localhost', user: 'root', password: 'ilensys#123', database: 'harness_db'}
);
var query = database.connect(function(err) {
if (err) throw err;
console.log("Connected!");
var sql = "INSERT IGNORE INTO harnes_detials (id, item_des, harness_drw_pin, harness_type, harness_conn_type, mating_crimp, design_volt, connectors_mating_mfg, mating_connector_part_no, design_current, gauge, connector_type, connect_gender, no_pins, no_rows, connectors, manufacture, contact_rating, voltage_rating, maximum_temp, connector_orientation, d_connector_type, d_connector_gender, d_mating_connector_part_no, d_connector_mating, d_connectors_mating_mfg, d_no_pins, d_no_rows,d_connector, d_manufacture, d_contact_rating, d_voltage_rating, d_maximum_temp, d_connector_orientation, d_guage, d_mating_crimp, createdAt, updatedAt) VALUES ? ";
var newSql = "LOAD DATA INFILE 'req.file.path' INTO TABLE harnes_detials";
var values = rows.slice(1);
// console.log(values);
database.query(sql, [values], function (err, result) {
// console.log(result);
if(err){
console.log(err);
}else{
res.render("success");
console.log( "row inserted: "+ result.affectedRows);
}
});
});
});
};
exports.uploadedSourceData = (req, res)=>{
const file = req.file;
// console.log(file);
if( file == undefined){
res.send("you can upload excel sheet only");
}
readXlsxFile(req.file.path).then((rows) =>{
var database = mysql.createConnection(
{ host: 'localhost', user: 'root', password: 'ilensys#123', database: 'harness_db'}
);
var query = database.connect(function(err) {
if (err) throw err;
console.log("Connected!");
var sql = "INSERT IGNORE INTO matingsources (id, mating_source_connectors,connector_type, source_connectors, gauge) VALUES ? ";
var newSql = "LOAD DATA INFILE 'req.file.path' INTO TABLE matingsources";
var values = rows.slice(1);
console.log(values);
database.query(sql, [values], function (err, result) {
console.log(result);
if(err){
console.log(err);
}else{
res.render("success");
console.log( "row inserted: "+ result.affectedRows);
}
});
});
});
};
below is my ejs file with inputs :
<form class="needs-validation" action="/uploadData" method="post" enctype="multipart/form-data" novalidate>
<div class="custom-file mb-3">
<input type="file" class="custom-select custom-file-input" id="filename" name="filename" required>
<label class="custom-file-label" for="customFile">Choose file</label>
<div class="valid-feedback">Looks Good!</div>
<div class="invalid-feedback">need to upload a file!</div>
</div>
<div class="mt-3 text-center">
<button type="submit" class="btn btn-primary">Upload</button>
<a class="btn btn-secondary" href="javascript:history.back()" role="button">Cancel</a>
</form>
<form class="needs-validation" action="/uploadSourceData" method="post" enctype="multipart/form-data" novalidate>
<div class="custom-file mb-3">
<input type="file" class="custom-select custom-file-input" id="filenameS" name="filenameS" required>
<label class="custom-file-label" for="customFile">Choose file</label>
<div class="valid-feedback">Looks Good!</div>
<div class="invalid-feedback">need to upload a file!</div>
</div>
<div class="mt-3 text-center">
<button type="submit" class="btn btn-primary">Upload</button>
<a class="btn btn-secondary" href="javascript:history.back()" role="button">Cancel</a>
</form>
its works fine with the first input upload option but with I upload from second input from other input its says :
multererror: unexpected field
but when I comment alternatively its works for both:
app.use(multer({ storage: fileStroge, fileFilter: fileFilter}).single('filename'));
// app.use(multer({ storage: fileStroge, fileFilter: fileFilter}).single('filenameS'));
but how to work with two different inputs uploads on the same page?? please help ??
Change the name attribute of the second input element to:
<input type="file" class="custom-select custom-file-input" id="filenameS" name="filename" required>
so that both input's have the same name attribute and then just use the multer route with 'filename'

cannot read property filename multer and node error

I have just started building this example of using multer with node js. I built a separate project which actually runs fine, but when I copied over the same setting making minor changes to routes on like from app.get to router.get, I am getting the error:
TypeError: Cannot read property 'filename' of undefined
here is my code for the route:
router.post('/userprofilepic', ensureAuthenticated, (req, res) => {
upload(req, res, (err) => {
if (err) {
res.render('/user/userdashboard', {
msg: err
});
} else {
const newImage = {
imageName: req.file.filename,
image_caption: req.body.image_caption,
member_id: req.user.member_id
}
new Image(newImage)
.save()
.then(image => {
req.flash('success_msg', 'Image added');
res.redirect('/user/userdashboard');
})
.then(resize => {
let imgHeight = 150;
let imgWidth = 150;
sharp(req.file.path)
.blur(.3)
.toFile('public/thumbs/blurred_' + req.file.originalname, function (err) {
if (!err) {
req.flash('success_msg', 'Thumbnail created');
//res.redirect('/');
}
});
})
.catch(error => {
console.log(error);
});
}
});
});
This is my multer configuration:
// Set storage engine
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: function (req, file, cb) {
cb(null, req.user.member_id + '_' + Date.now() + '_' + file.originalname);
}
});
// Init upload
const upload = multer({
storage: storage
}).single('imageName');
//load user model
require('../models/User');
const User = mongoose.model('users');
//load user profile model
require('../models/Profile');
const Profile = mongoose.model('profiles');
// Load images model
require('../models/Images');
const Image = mongoose.model('images');
and finally my form:
<form action="/user/userprofilepic" method="POST" enctype="multipart/form-data">
<div class="form-group">
<img id="blah" src="#" alt="your image" height="300" />
</div>
<div class="form-group">
<label for="exampleFormControlFile1">Example file input</label>
<input type="file" class="form-control-file" name="imageName" id="imageName">
</div>
<div class="form-group">
<label for="exampleInputEmail1">Image Caption</label>
<input type="text" class="form-control" name="image_caption">
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div class="form-group">
<input type="text" class="form-control" name="member_id" value="{{user.member_id}}" hidden>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
I tried console.log(req.file) and it's undefined. For the life of me I am unable to understand what I did wrong.
Please advise.
Figured it out...it was my mistake, in my server. js I had initialized an upload method which was overriding the multer upload function and hence no filename.

Angular2 - How to upload file with form data submit [(ngModel)] and save reference in MongoDB

I have this task page and want to upload a file with the form submit to the NodeJs express server.
#Component({
selector: 'tasks',
template: `<div mdl class="mdl-grid demo-content">
<div class="demo-graphs mdl-shadow--2dp mdl-color--white mdl-cell mdl-cell--8-col">
<h3>Create Task Page</h3>
<form action="#" (ngSubmit)="onSubmit()">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="taskname" [(ngModel)]="data.taskname"/>
<label class="mdl-textfield__label" for="taskname">Task Name</label>
</div> <br/>
<div class="mdl-textfield mdl-js-textfield">
<textarea class="mdl-textfield__input" type="text" rows= "5" id="taskdesc" [(ngModel)]="data.taskdesc"></textarea>
<label class="mdl-textfield__label" for="taskdesc">Task Description</label>
</div> <br/>
<select [(ngModel)]="data.assignedto">
<option *ngFor="#assign of dropdownValues" [ngValue]="assign.userEmail">{{assign.userEmail}}</option>
</select>
<div> <input type="file" placeholder="Upload file..."></div> <--How to handle this ?
<br/><br/> <button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" type="submit">Create Task</button>
</form>
</div>
`,
directives: [ROUTER_DIRECTIVES, MDL]
})
export class CreateTaskComponent implements OnInit {
data: any;
onSubmit(form) {
console.log(JSON.stringify(this.data));
this.apartmentService.postTasks(this.data).then(_=>this.router.navigate(['Tasks']));
this.data = {};
}
}
The data is posted to the expressjs server as follows
Server.ts
router.route('/api/newtask')
.post(function(req, res) {
var task = new Task();
task.taskname = req.body.taskname;
task.taskdesc = req.body.taskdesc;
task.assignedto = req.body.assignedto;
task.taskstatus = 'OPEN';
task.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Task created!' });
});
})
The mongoose mondel for the mongodb is as follows.
Mongoose Model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TaskSchema = new Schema({
taskname: String,
taskdesc: String,
taskcreator:String,
createddate: String,
assignedto: String,
taskstatus: String
});
module.exports = mongoose.model('Task', TaskSchema);
How can I upload a file and associate it with the current data on the form ?
plz see this article https://www.npmjs.com/package/multer
you can use multer in server side and very easy :
var express = require('express')
var multer = require('multer')
var upload = multer({ dest: 'uploads/' })
var app = express()
app.post('/profile', upload.single('avatar'), function (req, res, next) {
// req.file is the `avatar` file
// req.body will hold the text fields, if there were any
})
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
// req.files is array of `photos` files
// req.body will contain the text fields, if there were any
})
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {
// req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
//
// e.g.
// req.files['avatar'][0] -> File
// req.files['gallery'] -> Array
//
// req.body will contain the text fields, if there were any
})

Resources