Multer not populating request body - node.js

My web app is making a post request in the form of a multipart form with 2 text fields and one file.
I am able to access the file data perfectly fine via req.file, however the request body is always undefined.
I found some posts suggesting to re-arrange the fields so that the file is the last piece of data in the form... this did not solve the issue either!
Making the post request from front-end
uploadData(fileToUpload, xx, yy) {
const URL = 'http://localhost:5000/api/files/';
this.setState({ uploadingFile: true });
let formData = new FormData();
formData.append('testx', xx);
formData.append('testy', yy);
formData.append('file', fileToUpload);
fetch(URL, {
method: 'POST',
body: formData,
})
Back End handling of request
const multer = require('multer');
const upload = multer({
dest: 'labels/',
fileFilter: function (req, file, cb) {
if (file.mimetype !== 'application/pdf') {
return cb(null, false, new Error('Incorrect file type'));
}
cb(null, true);
},
limits: { fileSize: 100000 },
}).single('file');
...
...
...
router.post('/', checkRequestType, upload, (req, res) => {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
console.log('We got a multer error boys');
console.log(err);
return res.send('Error with multer');
} else if (err) {
console.log('Error - not caused by multer... but during upload');
return res.send('Unknown error during upload');
}
//Always null here?!?!
console.log(req.body);
});
});

There are a couple of issues here. The main one is that you are calling upload twice. The first as a middleware and then you are calling it a second time manually (so that you can handle errors).
You need to change
router.post('/', checkRequestType, upload, (req, res) => {
to this
router.post('/', checkRequestType, (req, res) => {
That should fix the null body issue.
A second issue is that you are passing too many parameters to the cb in this line return cb(null, false, new Error('Incorrect file type')). The first parameter should be the error: return cb(new Error('Incorrect file type'))

Related

Saving Images React To Nodejs

I am trying to upload an image from my front end to the backend but it it doesn't send the image in the request
It says that the formdata is empty and it says that there's no image found, where is the problem and how can I fix this error?
Here is the code from the Frontend made in react:
const [userInfo, setuserInfo] = useState({
file:[],
filepreview:null,
});
const handleInputChange = (event) => {
setuserInfo({
...userInfo,
file:event.target.files[0],
filepreview:URL.createObjectURL(event.target.files[0]),
});
}
const [isSucces, setSuccess] = useState(null);
const submit = async () =>{
const formdata = new FormData();
formdata.append('avatar', userInfo.file);
console.log(formdata)
Axios.post("http://localhost:4000/imageupload", formdata,{
headers: { "Content-Type": "multipart/form-data" }
})
.then(res => { // then print response status
console.warn(res);
if(res.data.success === 1){
setSuccess("Image upload successfully");
}
})
}
The code of the Backend made in NodeJS:
const storage = multer.diskStorage({
destination: path.join(__dirname, './temp', 'uploads'),
filename: function (req, file, cb) {
// null as first argument means no error
cb(null, Date.now() + '-' + file.originalname )
}
})
app.post('/imageupload', async (req, res) => {
try {
// 'avatar' is the name of our file input field in the HTML form
let upload = multer({ storage: storage}).single('avatar');
upload(req, res, function(err) {
// req.file contains information of uploaded file
// req.body contains information of text fields
if (!req.file) {
return res.send('Please select an image to upload');
}
else if (err instanceof multer.MulterError) {
return res.send(err);
}
else if (err) {
return res.send(err);
}
const classifiedsadd = {
image: req.file.filename
};
res.send("ok")
});
}catch (err) {console.log(err)}
})
Edit:
Multer is essentially a nodejs router,i.e. a function that can be pipelined between your HTTP request and HTTP response.
I think that you should first make multer analyze your HTTP content and to actually populate the req.file before actually evaluate express parsers do their job.
const storage = multer.diskStorage({
destination: path.join(__dirname, './temp', 'uploads'),
filename: function (req, file, cb) {
// null as first argument means no error
cb(null, Date.now() + '-' + file.originalname )
}
})
let upload = multer({ storage: storage});
app.post('/imageupload', upload.single('avatar'), async (req, res) => {
try {
// 'avatar' is the name of our file input field in the HTML form
// req.file contains information of uploaded file
// req.body contains information of text fields
if (!req.file) {
return res.send('Please select an image to upload');
}
else if (err instanceof multer.MulterError) {
return res.send(err);
}
else if (err) {
return res.send(err);
}
const classifiedsadd = {
image: req.file.filename
};
res.send("ok")
}catch (err) {console.log(err)}
})
I am assuming that your upload code is working. Have you tried to read the HTTP request from your browser to see that the image has been correctly attached to the request?
Because probably the issue lies in the fact that you are not actually parsing the image.
const file = new File(userInfo.file, "avatar.png", {
type: 'image/png' // choose the appropriate
});
const formdata = new FormData();
formdata.append('avatar', file);
console.log(formdata)

How To Send Files From One Nodejs Server To Another Nodejs Server using POST request and save into folder?

I want to send some image file from one Nodejs server to another Nodejs server. And how to get the file in second server? Also how to save into a folder in second server?
How to do that any suggestion?
First server
uploadImage(req, callback) {
var formData = new FormData();
var body = {
"file": req.file,
}
var options = {
'method': 'POST',
'url': config.db_layer_endpointUpload,
'headers': {
'api_key': config.db_layer_access_key,
'content-type': 'application/json'
},
body: JSON.stringify(body),
}
request(options, function (error, response) {
return callback(response.body);
})
}
Second server
app.post(
"/upload",
multerObj.single("file"),
(req, res) => {
console.log(req.body);
}
);
When console.log i am getting following result in Second server file
But Image is not saved in the asset folder. Multer and storage are fine. When i uploaded Image to Second server directly its working fine.
The first thing you need to do is create an API using node/Express.js and create store using multer:
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, 'uploads/');
},
// By default, multer removes file extensions so let's add them back
filename: function(req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() +
path.extname(file.originalname));
}
});
Build the image filter function:
const imageFilter = function(req, file, cb) {
// Accept images only
if (!file.originalname.match(/\.(jpg|JPG|jpeg|JPEG|png|PNG|gif|GIF)$/)) {
req.fileValidationError = 'Only image files are allowed!';
return cb(new Error('Only image files are allowed!'), false);
}
cb(null, true);
};
exports.imageFilter = imageFilter;
Create an API to handle image get from request:
app.post('/upload-pic', (req, res) => {
let upload = multer({ storage: storage, fileFilter: helpers.imageFilter }).single('pic');
upload(req, res, function(err) {
// req.file contains information of uploaded file
// req.body contains information of text fields, if there were any
if (req.fileValidationError) {
return res.send(req.fileValidationError);
}
else if (!req.file) {
return res.send('Please select an image to upload');
}
else if (err instanceof multer.MulterError) {
return res.send(err);
}
else if (err) {
return res.send(err);
}
// Display uploaded image for user validation
res.send(`You have uploaded this image`);
});
});
Now you have the server side accept the image from request and save it on file. After that, let us go back to the other server. On other server it's like a client and we need create request to the API upload-pic . To do that you can use axios package and form-data package.
Handling File Uploads

Uploading file with multer (NodeJS) + other form data on same POST request (Angular)

I'm submitting a form on an Angular build front-end.
The form has both normal text input fields and a file upload function.
I POST both the text input fields to my NodeJS API as a JSON object "contact", as well as the file as a new FormData as such:
// 'contact' defined above as a JSON object
// 'profilePic' set from event.target.files[0] in a listener function
const profilePicData = new FormData();
profilePicData.append('file', profilePic);
return this.http
.post<ContactResponseData>('API_URL_HERE',
{ contact, profilePicData } ...
And then capture it from my API as such:
router.post("/", upload.single('file'),(req, res) => {
console.log("REQ: "+ req);
console.log("BODY: " + JSON.stringify(req.body));
console.log("FILE: " + req.file);
The req.file is "undefined", i.e. null, and my req.body has a "profilePicData" key value pair which is empty. I assume this is because the entire form gets submitted as JSON and not as multipart form data.
But I can't google anything helpful around how to send both JSON and multipart to my API as one POST request so that both req.body and req.file pick up the right information. I guess understanding the theory and best practices behind what's going here is what I'm after. Should I have two POST urls, one for JSON and one for file? Or should I be submitting my JSON as multipart as well (how do I do that in Angular)? Any help is appreciated.
You will have to send everything as multipart by adding fields to an instance of FormData and send it as the payload.
const form = new FormData();
form.append('file', profilePic);
form.append('contact', contact);
...
return this.http.post<ContactResponseData>('API_URL_HERE', form, ...)
I used the below method in React
Below is how I created input
<form className={styles.root} noValidate autoComplete="off">
<input
name="avatar_image" // name of input field or fieldName simply
enctype="multipart/form-data"
type="file"
onChange={(event) => {
// console logging selected file from menu
console.log( event.target.files[0] ) // gives first file
// setState method with event.target.files[0] as argument
this.setState(prev => ({...prev, user_image: event.target.files[0]}))
}}
/>
</form>
Below is how I made requests to backend
const formData = new FormData()
formData.append('user_name', this.state.user_name)
formData.append('phone_number', this.state.phone_number)
// now below avatar_image is the fieldName of the image, then comes the file to upload, and the file name in the end
formData.append('avatar_image', this.state.user_image, this.state.user_image.name)
axios.post(utils.baseUrl + '/avatar-uploads/avatar-image-upload', formData, {
onUploadProgress: progressEvent => {
console.log( 'upload progress: ' + Math.round((progressEvent.loaded / progressEvent.total)*100) + '%' )
}
})
.then(function (response) {
// further code
})
.catch(function (error) {
console.log(error);
});
Below is how I handled the backned with multer, including dealing with payload
const image_storage = multer.diskStorage({
destination: path.join(__dirname , '../../assets/images/uploads/avatar_image'),
filename: function(req, file, cb){
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname))
}
});
// Init Upload
const user_avatar_image_upload = multer({
storage: image_storage,
limits:{fileSize: 2000000}, // 1 mb
fileFilter: function(req, file, cb){
checkFileTypeForUserAvatar(file, cb);
}
}).single('avatar_image'); // this is the fieldName that will be dealt
// Check File Type
function checkFileTypeForUserAvatar(file, cb){
// Allowed ext
let filetypes = /jpeg|jpg|png|gif/;
// Check ext
let extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
let mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
} else {
cb('Error: jpeg, jpg, png, gif Images Only!');
}
}
// router.post('/protected-avatar-image-upload', passport.authenticate('jwt', { session: false }), (req, res, next) => {
router.post('/avatar-image-upload', (req, res, next) => {
console.log(req.body) // here the req.body will turn out {}
user_avatar_image_upload(req, res, (err) => {
if(err){
console.log(err)
} else {
if(req.file == undefined){
res.status(404).json({ success: false, msg: 'File is undefined!',file: `uploads/${req.file.filename}`})
} else {
console.log( req.body.user_name ) // here req.body.user_name and others will work
// further code
res.status(200).json({ success: true, msg: 'File Uploaded!',file: `uploads/${req.file.filename}`})
}
}
})
})
Hope this helps how to upload a file using multer, with additional payload being passed so that it can be utilized as well with creating database entries or anything else.

A super simple Multer question, nested file uploading

I'm trying to do an image upload with Multer and Express. Doing just an image upload is going fine and everything works, the problem is, I want to send more than just one photo. Let me explain. I got the most basic form ever, not worth sharing. Then when submitting the form with JUST an image, the axios request looks like this:
async onSubmit() {
const formData = new FormData();
formData.append('file', this.person.personData.file)
this.obj.file = formData
try {
await axios.post('/projects/new', this.obj.file);
this.message = 'Uploaded';
} catch (err) {
console.log(err);
this.message = 'Something went wrong'
}
},
The post route in Express to receive the image looks like this:
personRoutes.post('/new', upload.single('file'), (req, res) => {
console.log('BODY: ', req.body)
console.log('REQ.FILE: ', req.file)
const person = new Person({
personData: {
file: req.file.path
}
});
person.save()
.then(result => {
console.log('YES', result)
res.redirect('/projects')
})
.catch(err => {
console.log('KUT', err)
})
});
req.file is the upload.single('file') file. Req.body will hold the text fields, if there were any. Ez Pz, so far so good. Now, where things get a bit sketchy is, what if I wanted to upload more than just one photo? So my obj object would not only hold a file property but a few others aswell. Currently I am directly sending the formData file with
await axios.post('/projects/new', this.obj.file);
But if my obj contained more than just a file, I would have to to this:
await axios.post('/projects/new', this.obj);
But what in the world should my Express post route look like? Because req.file will now (as far as I know) forever be undefined because file is not defined inside req object. It is defined in req.body object. Accessing the file as req.obj.file won't do anything. Any help would be very much appreciated. I don't even know if it is possible. And if not, what other options do I have?
Thanks in advance!
upload.array('file') should work. any number of files will be received.
here is an example:
multer code:
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads')
},
filename: (req, file, cb) => {
cb(null, "image"+Date.now()+file.originalname);
}
});
const fileFilter = (req,file,cb)=>{
if(file.mimetype==="image/jpeg" || file.mimetype==="image/png"){
cb(null, true);
}else{
cb(new Error("File type is not acceptable"),false);
}
}
const uploadImages = multer({storage:storage,
limits:{
fileSize: 1024*1024*10
},
fileFilter:fileFilter}).array("shopImage");
app.post code:
app.post("/shop", function(req,res){
uploadImages(req,res,function(err){
if(err){
console.log(err);
res.status(400).json({message:err.message});
}else{
console.log(req.files);
console.log(req.body);
....
}
});
....
})

Express, Nodejs use Multer for different Services

I am using express server and multer for upload file on different services (local, azure, cloudinary, amazon s3 etc).
For that i am using different module of multer multer-azure, multer-cloudinary etc.
I need this configuration will be applied to user wise and that information comes from the database.
So i need a extra call to fetch data from database before multer come in action.
I am able to call database query but when i am trying to call multer function, req parameter coming blank. Here is what i am doing.
var multerUtility = require('./upload/multer.utility');
let multer = new multerUtility().getActiveMulterService();
router.post('/', getMetadataConfiguration, multer, (req, res, next) => {
console.log('========== req ==========', req.file); // It is coming blank
console.log('========== req ==========', req.body); // It is coming blank
});
Here is first middleware function, which fetch data from database to verifiy which service will use to upload file.
function getMetadataConfiguration(res, req, next) {
var conn = new jsforce.Connection({
loginUrl : config.org_url,
});
var records = [];
conn.login(username, password, function(err, userInfo) {
if (err) {
return console.error(err);
}
conn.query("query", (err, result) => {
if(err) {
res.status(500).send(err);
}
console.log('=========== result=========', result);
req.serviceConfig = result.records[0];
next();
});
});
}
And here is my MulterUtility Class to handle configuration:
upload/multer.utility.js
class MulterUtility {
constructor() {
}
getActiveMulterService(req, res, next) {
var multerConfiguration;
if(req.serviceConfig.service == 'azure') {
multerConfiguration = multer({
storage: multerAzure({
connectionString: config.azure.connectionString,
account: config.azure.account,
key: config.azure.key,
container: config.azure.container
})
}).single('image');
} else if(req.serviceConfig.service == 'cloudinary') {
multerConfiguration = multer({
storage: cloudinaryStorage({
cloudinary: cloudinary,
folder: config.storageFolder
// allowedFormats: ['jpg', 'png', 'jpeg']
})
}).single('image');
} else if(req.serviceConfig.service === 'amazon') {
multerConfiguration = multer({
storage: multerS3({
s3: s3,
bucket: 'mycontainer',
acl: 'public-read',
contentType: multerS3.AUTO_CONTENT_TYPE,
metadata: function (req, file, cb) {
cb(null, {fieldName: file.fieldname});
},
key: function (req, file, cb) {
cb(null, Date.now().toString() + '-' + file.originalname)
}
})
}).single('image');
} else if(req.serviceConfig.service === 'local') {
multerConfiguration = multer({
storage: multer.memoryStorage()
}).single('image');
}
return multerConfiguration;
}
}
module.exports = MulterUtility;
After executing multer, i am not recieving a req.file or req.body params what multer sets after uploading file.
For now you can consider the 'local' file upload as mentioned in last condition.
The problem is that you call the method getActiveMulterService once before the router. But you need to call it for each post. Try something like this:
var multerUtility = require('./upload/multer.utility');
let multers = new multerUtility();
router.post('/',
getMetadataConfiguration,
(req, res, next) => multers.getActiveMulterService(req, res, next)(req, res, next),
(req, res, next) => {
console.log('========== req ==========', req.file); // It is coming blank
console.log('========== req ==========', req.body); // It is coming blank
});
And in this function you have arguments in the wrong order:
getMetadataConfiguration(res, req, next)
// ==>
getMetadataConfiguration(req, res, next)
Hi this has largely been answered already. The solution is to manual add your file object back onto your req.body object during the process.
Full solution is found here

Resources