Upload file using mutler without disabling bodyparser in NEXTJS - node.js

I am trying to upload an image file using mutler along with some form data. Every solution I've looked suggests to disable the bodyparser but if I disable the body parser then I cannot parse the request body.
Backend POST api along with function that runs middleware:
case 'POST':
if (req.body instanceof FormData) {
await runMiddleware(req, res, upload.single('image'))
}
console.log('ID: ',req.query.id)
Courses.findByIdAndUpdate(req.query.id, req.body, {new: true}, (err, result) => {
if(err)
{
console.log('Error in findByIdAndUpdate: ',err)
res.status(400).json({ success: false, data: result })
}
else {
console.log('Success in findByIdAndUpdate: ',result)
res.status(200).json({ success: true, data: result })
}
})
below is the function
function runMiddleware(req, res, fn) {
return new Promise((resolve, reject) => {
fn(req, res, (result) => {
if (result instanceof Error) {
return reject(result)
}
return resolve(result)
})
})
}
Upload Middleware
import multer from "multer";
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "public/uploads");
},
filename: (req, file, cb) => {
cb(null, "upload" + Date.now() + "-" + file.originalname)
}
})
module.exports = multer({storage})
Front-End API call
const createUpdateFormData = () => {
const data = {
title: getValues('title'),
categories: getValues('categories'),
description: getValues('description'),
image: (getValues('file') ? getValues('file')[0] : null),
}
let formData;
if (data.image) {
formData = new FormData();
Object.entries(data).forEach(([key, value]) => {
if(value)
formData.append(key, value);
});
}else {
formData = {}
Object.entries(data).forEach(([key, value]) => {
if(value)
formData[key] = value;
});
}
return formData
}
const handleUpdateRequest = (id,formData) => {
console.log(formData)
const updateReq = axios.post(`/api/courses/${id}`, formData)
toast.promise(updateReq, {
loading: "Processing...",
error: (err) => {
console.log(err)
if (err.response.status === 409)
return 'Course already exists!'
else
return 'Oops something went wrong!'
},
success: (res) => {
console.log(res)
const newState = courseState.map(course => course._id === id ? res.data.data : course)
setCourses(newState)
// console.log('NewState: ', courseState)
return "Course updated successfully!"
}
});
}
I am using the useForm hook

Related

React Native && Node js How to upload image

I am having trouble uploading an image from my react native app. I have a react app that uploads images to the backend from camera as a file.
let pic = await camera.current.takePictureAsync({
quality: 1,
base64: true,
});
setPicture(pic);
Then
onPress={() => {
const formData = new FormData();
formData.append("file", {
name: picture + "",
type: picture.type,
uri:
Platform.OS === "ios"
? picture.uri.replace("file://", "")
: picture.uri,
});
console.log(formData);
httpClient
.post("demo/testsaveimg", formData)
.then((request) => {
if (request.data == "ok") {
console.log(request.data);
}
});
}}
This is my Formdata to service
and then, here is my service
app.post('/testsaveimg', async function (req, res, next) {
let storageUploadFile = multer.diskStorage({
destination: (req, file, next) => {
const folder = './test/'
if (!fs.existsSync(folder)) {
fs.mkdirSync(folder)
}
next(null, folder)
},
filename: (req, file, next) => {
const ext = file.mimetype.slice("/")[1]
next(null, `${file.fieldname}-${Date.now()}.${ext}`)
}
})
let UploadFile = multer({ storage: storageUploadFile }).single("files");
UploadFile(req, res, async function (err) {
console.log(req.file);
if (err instanceof multer.MulterError) {
return res.status(500).json(err);
} else if (err) {
console.log(99);
return res.status(500).json(err);
}
if (req.file != undefined) {
console.log("ok");
res.json("ok")
} else {
console.log("no");
res.json("no")
}
})
})
But there are no pictures in the folder "./test/" and response is "no"
that means "req.file = undefined"
What steps am I doing wrong?
or is there another way to write?

Nodejs formidable async mongoose await issue

I'm trying to make a OCR parse of an image.
All the things works well but I have a problem this mongoose and syncronysm.
But cannot use "await" on the mongoose find call as the function is not async. How do I solve that.
Here is my code:
// post processImage
router.post('/', async (req, res) => {
try {
var baseUrl;
const form = formidable({ multiples: true });
form.parse(req, function (error, fields, files) {
var imatgeAProcessar = files.image.path;
var extname = path.extname(files.image.name);
getTextFromImage(imatgeAProcessar) // OCR process of the image
.then(res => {
const boss_name_req = res.boss_name;
const boss = Boses.findOne({"name" : boss_name_req}).exec();
// ERROR HERE // return nothing althought it exist on database (no await?)
console.log(JSON.stringify(boss)); // writes "{}"
const processedImage = {
"success": true,
"boss_name": boss.name,
"boss_image": baseUrl + 'images/' + boss.num + ".png"
}
res.json(processedImage);
})
});
} catch (err) {
res.json({message: err});
}
});
*edited
// post processImage
router.post('/', async(req, res) => {
try {
var baseUrl;
const form = formidable({ multiples: true });
var formfields = await new Promise(function(resolve, reject) {
form.parse(req, function(err, fields, files) {
if (err) {
reject(err);
return;
}
resolve(files);
}); // form.parse
});
var imatgeAProcessar = formfields.image.path;
var extname = path.extname(formfields.image.name);
const res = await getTextFromImage(imatgeAProcessar)
const boss_name_req = res.boss_name;
const boss = await Boses.findOne({ "name": boss_name_req }).limit(4).skip(0).exec();
const processedImage = {
"success": true,
"boss_name": boss.name,
"boss_image": baseUrl + 'images/' + boss.num + ".png"
}
res.json(processedImage)
} catch (err) {
res.json({ message: err });
}
});
Finally I found the way... I wrote a callback on the findOne call as :
const boss = Boss.findOne({"name" : boss_name_req})
.then( resMongoose => {
try {
const processedImage = {
"success": true,
"gym": resOCR.gym,
"boss_name": resMongoose.name,
}
res.json(processedImage);
} catch (err) {
res.json({message: err});
}
});

Axios POSTed FormData has empty body on serverside

This is the client side code. Data is not empty, file is getting uploaded correctly.
export function addGame(data) {
return dispatch => {
const formData = new FormData();
formData.append("game.cover", data.gameCover[0]);
formData.append("game.title", data.gameTitle);
formData.append("game.price", data.gamePrice);
formData.append("game.description", data.description);
return axios.post(apiUrl + "/games/add", formData).then(res => {
dispatch({ type: ADD_GAME, payload: res.data.game });
});
};
}
and this is the serverside
router.post("/add", auth, async (req, res) => {
const body = await req.body;
console.log(body);
let formErrors = [];
if (!body.gameTitle) formErrors.push("Game title is required.");
if (!body.description) formErrors.push("Description is required.");
if (!body.gamePrice) formErrors.push("Price is required.");
if (formErrors.length) res.status(400).send({ success: false, formErrors });
else {
let gameCoverFileName;
if (!fileUpload(req, gameCoverFileName))
formErrors.push("Failed to upload file");
const result = await gameModel.create({
title: body.gameTitle,
cover: gameCoverFileName,
price: body.gamePrice,
description: body.description
});
if (result)
res.status(201).send({
success: true,
game: {
gameTitle: result.title,
gameCover: gameCoverFileName,
gamePrice: result.price,
description: result.description
}
});
} });
And I'm getting empty body
You need to additionally process the multipart form-data. For example with multiparty:
const multiparty = require("multiparty");
router.post("/add", auth, async (req, res) => {
try {
const parse = function (req) {
return new Promise(function(resolve, reject) {
const form = new multiparty.Form()
form.parse(req, function(err, fields, files) {
!err ? resolve([fields, files]) : reject(err)
})
})
}
const [body] = await parse(req)
console.log(body)
} catch (err) {
console.log(err)
}
res.json("ok")
})

Adding req object to Kue job

Been hunting the internet trying to find an answer to why the following doesn't work.
I am trying to pass in the req object when I add the job so that I have access to it when the job is processed.
But the process is never executed when the whole req object is passed to job.data. Yet I can pass parts of the req object.
What I'm trying to do maybe anti-pattern and a big no no. But, I am trying to understand why it won't work. It seems strange that it just continues without any error.
Below is an example, hopefully it is clear.
My kue is abstracted into a separate file, and initialised onto app.locals.Q as follows:
// Q.js
class Q {
constructor(options) {
this.q = kue.createQueue(options)
}
addJob = (name, data) => {
return Queue.create({
queue_job: name,
queue_route: data.route,
queue_user: data.user,
queue_added: new Date(),
})
.then(response => {
this.q.create(name, {
id: response.get('queue_id'),
route: data.route,
request: data.request
})
.save();
return Promise.resolve(response);
})
.catch(error => {
return Promise.reject(error);
});
processJob = (name, work, options = {}) => {
const {concurrency} = options;
this.q.process(name, concurrency || 1, (job, done) => {
const {data: {id, route, request}} = job;
Queue.update({
queue_running: true
}, {
where: {
queue_id: id
}
})
.then(() => {
if (process.env.NODE_ENV !== 'production') {
console.log(`running job ${id} from ${route}`);
}
return new Promise((resolve, reject) => {
return work(resolve, reject, request);
});
})
.then(results => {
return Queue.update({
queue_running: false,
queue_completed: new Date(),
queue_results_path: results || null
}, {
where: {
queue_id: job.data.id
}
});
})
.then(() => {
if (process.env.NODE_ENV !== 'production') {
console.log(`completed job ${id} from ${route}`);
}
done();
})
.catch((error) => {
if (process.env.NODE_ENV !== 'production') {
console.log(`failed job ${id} from ${route}`);
console.log(error);
}
Queue.update({
queue_running: false,
queue_error: `${error}`
}, {
where: {
queue_id: id
}
})
.then(() => {
done(error);
})
.catch(err => {
console.error(err);
done(err);
});
});
});
};
};
// example route
queue = (req, res) => {
const {locals: {Q}} = req.app;
Q.addJob('foo', {
route: req.path,
user: req.user.get('username'),
request: req
})
.then(queue_id => {
Q.processJob('foo', (resolve, reject, request) => {
console.log(request)
resolve('complete')
})
res.json({sucess: true})
})
}
redis can't serialize the req object.
kue simply silently fails.

Mongoose remove and create in get route

I have a small issue with mongoose, what I am doing is getting data from online rss feeds, parsing it, and passing it to an array, from which I feed a mongoose model, and all this happens in the get route, what I want to accomplish is delete all the data first from the mongoose model and then populate it with the new data, but it always either deletes the data all together, since the parser iterates a few times, or it doesn't delete anything and the data just keeps adding to the model.
Here's my code
'use strict';
const Promise = require('bluebird');
const request = require('request');
const FeedParser = require('feedparser');
const express = require('express');
const router = express.Router();
const xray = require('x-ray')();
var Post = require('../models/post');
var dataArray = [];
router.get('/', function (req, res) {
const fetch = (url) => {
return new Promise((resolve, reject) => {
if (!url) {
return reject(new Error(`Bad URL (url: ${url}`));
}
const feedparser = new FeedParser();
const items = [];
feedparser.on('error', (e) => {
return reject(e);
}).on('readable', () => {
// This is where the action is!
var item;
console.time('loading')
while (item = feedparser.read()) {
items.push(item);
}
}).on('end', () => {
resolve({
meta: feedparser.meta,
records: items
});
});
request({
method: 'GET',
url: url
}, (e, res, body) => {
if (e) {
return reject(e);
} else if (res.statusCode != 200) {
return reject(new Error(`Bad status code (status: ${res.statusCode}, url: ${url})`));
}
feedparser.end(body);
feedparser.on('end', function () {
console.log('Done');
});
});
});
};
Promise.map([
'url',
'url',
'url',
'url'], (url) => fetch(url), { concurrency: 4 }) // note that concurrency limit
.then((feeds) => {
feeds.forEach(feed => {
feed.records.forEach(record => {
dataArray.push(record);
});
});
}).catch(function (error) {
console.log(error);
});
Post.remove({}, function (err) {
if (err) {
console.log(err);
} else {
console.log('collection removed');
}
});
dataArray.forEach(post => {
Post.create({
title: post.title,
content: post.description,
created: post.date,
image: post['rss:image']['#'],
link: post.link
}, function (err, newPost) {
console.log(newPost.title);
});
});
Post.find({}, function (err, posts) {
if (err) {
console.log(err);
} else {
res.render('index/home', {
posts: posts
});
}
});
});
module.exports = router;
None of this is going to run synchronously. You can do Something like this :
'use strict';
const Promise = require('bluebird');
const request = require('request');
const FeedParser = require('feedparser');
const express = require('express');
const router = express.Router();
const xray = require('x-ray')();
var Post = require('../models/post');
var dataArray = [];
const fetch;
router.get('/', function (req, res) {
Post.remove({}, function (err) {
if (err) {
console.log(err);
} else {
console.log('collection removed. Starting to fetch Posts from Service');
fetch = (url) => {
return new Promise((resolve, reject) => {
if (!url) {
return reject(new Error(`Bad URL (url: ${url}`));
}
const feedparser = new FeedParser();
const items = [];
feedparser.on('error', (e) => {
return reject(e);
}).on('readable', () => {
// This is where the action is!
var item;
console.time('loading')
while (item = feedparser.read()) {
items.push(item);
}
}).on('end', () => {
resolve({
meta: feedparser.meta,
records: items
});
});
request({
method: 'GET',
url: url
}, (e, res, body) => {
if (e) {
return reject(e);
} else if (res.statusCode != 200) {
return reject(new Error(`Bad status code (status: ${res.statusCode}, url: ${url})`));
}
feedparser.end(body);
feedparser.on('end', function () {
console.log('Done');
});
});
});
};
}
});
Promise.map([
'url',
'url',
'url',
'url'], (url) => fetch(url), { concurrency: 4 }) // note that concurrency limit
.then((feeds) => {
feeds.forEach(feed => {
dataArray = dataArray.concat(feed.records);
/*feed.records.forEach(record => {
dataArray.push(record);
});*/
});
console.log('inserting posts in the collection');
dataArray.forEach(post => {
Post.create({
title: post.title,
content: post.description,
created: post.date,
image: post['rss:image']['#'],
link: post.link
}, function (err, newPost) {
console.log(newPost.title);
});
});
console.log("Fetching posts from the collection");
Post.find({}, function (err, posts) {
if (err) {
console.log(err);
} else {
res.render('index/home', {
posts: posts
});
}
});
}).catch(function (error) {
console.log(error);
});
});
module.exports = router;
I haven't tested this. Please test it on your end. Let me know if there's an error or something.

Resources