NodeJS - Upload using Multer with a Promise - node.js

I've been rewriting my very simple server lately. I have been introducing promises and the final part of my rewrite is implementing my post methods which involves using Multer for uploading images.
This is what I have so far. The GET works perfect, as does the POST when I remove the image upload part of it.
Unfortunately when I introduce Multer I get this error:
TypeError: Cannot convert object to primitive value
at exports.format (util.js:91:18)
at Console.log (console.js:46:37)
at Object.createACategory (/root/api/controllers/controller.js:34:18)
Here is my Route class where I am calling the POST:
'use strict';
module.exports = function(app, gameCategoryList) {
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, '/var/www/html/uploadsCategoryIcon');
},
filename: function (req, file, callback) {
var originalname = file.originalname;
var extension = originalname.split(".");
callback(null, extension[0] + '-' + Date.now() + '.' + extension[extension.length-1]);
}
});
var upload = multer({ storage: storage });
app.get('/api/gameCategories', (req, res) => {
gameCategoryList.listAllCategories().then(category => res.json(category));
});
app.post('/api/createGameCategory', upload.single('gameCategoryImage'), (req, res) => {
gameCategoryList.createACategory(req.body, req.file).then(category => res.json(category));
});
};
This is what the method looks like in my controller class:
createACategory(body, file) {
var splitPath = file.path.split("html");
var imagePath = 'example.com' + splitPath[1];
var new_category = new Category({
categoryName: body.categoryName,
categoryTag: body.categoryTag,
categoryImageUrl: imagePath
});
return new Promise(function(resolve, reject) {
new_category.save(function(err, category) {
if (err) {
return reject(err)
} else {
return resolve(category)
}
});
}).then((category) => {
if(category)
{
return category
}
else
{
return { success: false }
}
});
}
UPDATE:
On line 34 of controller.js is:
var new_category = new Category({
categoryName: body.categoryName,
categoryTag: body.categoryTag,
categoryImageUrl: imagePath
});
When I remove the image part of this code (categoryImageUrl: imagePath) and don't pass any file or image, this code works fine.

Related

req.body is empty and req.files is undefined when I try to upload a file with multer

I'm trying to send FormData to nodeJs. I appended the values 'id' and 'photo' to the form data. In the front-end, I can clearly see that the FormData is there in the browser's console log, but it is undefined in the nodeJs backend
the backend:
const random = Math.floor(Math.random() * 100000) + 10000;
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './client/public/images');
},
filename: function (req, file, cb) {
cb(null, random.toString() + new Date().getTime() + '.jpg');
},
});
const move = multer({ storage: storage });
const upload = move.single('photo');
router.get('/upload-photo', upload, (req, res) => {
const id = req.body.id;
const photo = req.files['photo'][0].filename;
db.query(
'UPDATE users SET photo = ? WHERE id = ?',
[photo, id],
function (err, rows, fields) {
if (err) throw err;
if (rows.length >= 1) {
rows.map((entry) => {
const user = {};
COLUMNS.forEach((c) => {
user[c] = entry[c];
});
const theUser= {
id: user.id,
photo: user.photo,
};
res.json(theUser);
});
} else {
return res.status(200).json({});
}
}
);
});
The function :
function photoUpload() {
const photoData = new FormData();
photoData.append('id', id);
photoData.append('photo', photoFile);
dispatch(uploadPhoto(photoData));
}
the uploadPhoto action:
export const uploadPhoto = (photoData) => (dispatch) => {
axios
.get(`/api/user/upload-photo`, photoData)
.then((res) => {
dispatch(getPhotos());
})
.catch((err) => {
let message = typeof err.response != 'undefined' ? err.response.data : {};
dispatch({
type: GET_ERRORS,
payload: message,
});
});
};
I don't know if it matters or not, but there is another route like this in a different api file and it works fine. This is basically the same code as that one with the only difference being that the other route uploads multiple files along with multiple req.body data. And that one works perfectly
Instead of:
const photo = req.files['photo'][0].filename;
I had to do:
const photo = req.file.filename; since it was just only one file

Problem to change request from get to post and change destination of upload file dynamic in multer nodejs

Hey i work with multer package in node js to upload file.
I make it dynamic but the request is in get and i have problem to change it to post.
Its only work to me in get request.
What it doing now?
It save the file in new folder that call flow and take the id from the client.
I need to replace it to post and get the id in post but i want to move the logic to the route and make it stay same. thanks
My Code:
fileupload service:
const path = require("path");
const multer = require("multer");
const crypto = require("crypto");
const models = require("../models");
const fsex = require("fs-extra");
const today = new Date();
const date =
today.getFullYear() + "-" + (today.getMonth() + 1) + "-" + today.getDate();
const storage = multer.diskStorage({
destination: (req, file, callback) => {
let flowID = req.params.flowID;
let path = `./uploads/flow/${flowID}`;
fsex.mkdirsSync(path);
callback(null, path);
},
filename: (req, file, cb) => {
crypto.pseudoRandomBytes(8, (err, raw) => {
if (err) return cb(err);
cb(
null,
date + "_" + raw.toString("hex") + "_" + path.extname(file.originalname)
);
});
}
});
const upload = multer({ storage: storage }).single("upload");
module.exports = {
upload: upload,
};
my route:
router.get("/uploadfile/:flowID", upload, (req, res, next) => {
const file = req.file;
const flowID = req.params.flowID;
if (!file) {
const error = new Error("Please upload a file");
error.httpStatusCode = 400;
return next(error);
} else {
createFileInDB(file.originalname, flowID, file.filename)
.then(() => {
console.log("File Created");
res.json(file);
})
.catch(err => {
res.status(500).send(err);
});
}
});

How to upload file using Koa?

I am setting up a new server and want to support advanced upload function. Firstly I need to validate file (filetype, filesize, maxcount), and finally upload it to some destination. I tried something with koa-multer but I cannot get multer validation errors.
multer.js
const multer = require('koa-multer')
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/uploads/')
},
filename: function (req, file, cb) {
var fileFormat = (file.originalname).split('.')
cb(null, file.fieldname + '_' + Date.now() + '.' + fileFormat[fileFormat.length - 1])
}
})
const fileFilter = (req, file, cb) => {
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true)
} else {
cb(new Error('This is not image file type'), false)
}
}
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 1,
files: 5
},
fileFilter: fileFilter
})
module.exports = upload
router.js
const Router = require('koa-router')
const multer = require('../middlewares/multer')
const auth = require('../middlewares/auth')
const controller = require('../controllers').userController
const schemas = require('../schemas/joi_schemas')
const validation = require('../middlewares/validation')
const router = new Router()
const BASE_URL = `/users`
router.post(BASE_URL, auth , validation(schemas.uPOST, 'body'), controller.
addUser)
router.put(`${BASE_URL}/:id`, auth , multer.single('logo')(ctx, (err) => {
if (err) {
ctx.body = {
success: false,
message: 'This is not image file'
}
}
}), controller.editUser)
router.delete(`${BASE_URL}/:id`, auth , controller.deleteUser)
module.exports = router.routes()
How can I solve upload problem this in best way for long term maintain of code?
The simplest approach for uploading files is the following (assume the form has a file upload field called avatar:
const Koa = require('koa')
const mime = require('mime-types')
const Router = require('koa-router')
const koaBody = require('koa-body')({multipart: true, uploadDir: '.'})
const router = new Router()
router.post('/register', koaBody, async ctx => {
try {
const {path, name, type} = ctx.request.files.avatar
const fileExtension = mime.extension(type)
console.log(`path: ${path}`)
console.log(`filename: ${name}`)
console.log(`type: ${type}`)
console.log(`fileExtension: ${fileExtension}`)
await fs.copy(path, `public/avatars/${name}`)
ctx.redirect('/')
} catch(err) {
console.log(`error ${err.message}`)
await ctx.render('error', {message: err.message})
}
})
Notice that this example uses Async Functions which allows for clean code with a standard try-catch block to handle exceptions.
koa middleware is like a nested callback, you should catch "next()" not after multer
router.put(`${BASE_URL}/:id`, auth , async (ctx, next) => {
try{
await next()
} catch(err) {
ctx.body = {
success: false,
message: 'This is not image file'
}
}
}, multer.single('logo'), controller.editUser)
but you do this, it will catch controller.editUser errors too which not been caught by controller itself.
You can use one of two options:
The first is by adding callback function to the end of the route.
const multer = require('#koa/multer')
//Options to limit file size and file extension
const upload = multer({
dest: '../avatars',
limits: {
fileSize: 1024*1024
},
fileFilter(ctx, file, cb) {
if (!file.originalname.match(/\.(jpg|jpeg|png)$/)) {
return cb(new Error('Please upload a World document'))
}
cb(undefined, true)
}
})
//The last callback should handle the error from multer
router
.post('/upload', upload.single('upload'), async (ctx) => {
ctx.status = 200
}, (error, ctx) => {
ctx.status = 400
ctx.body = error.message
})
})
The second option is by adding try/ catch before calling multer middleware:
router
.post('/upload', async (ctx, next) => {
try {
await next()
ctx.status = 200
} catch (error) {
ctx.status = 400
ctx.body = error.message
}
}, upload.single('upload'), async ctx => {ctx.status = 200})
In last case if exception will thrown in multer, it will be handled by try/catch in previous await next()

Express Multer validate fields before upload

I have been seen this : NodeJS Multer validate fields before upload but is not work. i tried for make validate field before upload in a days and the result is not working. is it because mutler can't do this?
Iam using MERN : MongoDB,React,Express,Node
i want validate this data in Form-data : dataMurid before uploading the image.
is there another way to overcome this? Maybe using other library? and please give me exampale code.
Route
// #route buat edit/create murid baru
router.post('/datasiswa/create',(req, res,next) => {
upload(req,res,(err)=>{
let request = JSON.parse(req.body.newMurid);
// upload(req.body.data.data, res, (err) => {
// console.log(req.body.data.data);
// });
const { errors, isValid } = validateMuridInput(request);
// Check validation
if (!isValid) {
return res.status(400).json(errors);
}
const muridFields = {};
if (request.tempatLahir) muridFields.tempatLahir = request.tempatLahir;
if (request.jenisKelamin) muridFields.jenisKelamin = request.jenisKelamin;
if (request.nis) muridFields.nis = request.nis;
if (request.nama) muridFields.nama = request.nama;
if (request.tanggalLahir) muridFields.tanggalLahir = request.tanggalLahir;
if (request.namaAyah) muridFields.namaAyah = request.namaAyah;
if (request.namaIbu) muridFields.namaIbu = request.namaIbu;
if (request.noTelepon) muridFields.noTelepon = request.noTelepon;
if (request.hpSiswa) muridFields.hpSiswa = request.hpSiswa;
if (request.hpIbu) muridFields.hpIbu = request.hpIbu;
if (request.hpAyah) muridFields.hpAyah = request.hpAyah;
if (request.alamat) muridFields.alamat = request.alamat;
Murid.findOne({ nis: request.nis })
.then((murid) => {
if (murid) {
errors.nis = 'NIS ini sudah terdaftar';
return res.status(400).json(errors);
} else {
const newMurid = new Murid(muridFields);
newMurid.save()
.then((murid) => {
res.json(murid);
})
.catch((err) => {
console.log(err);
});
}
})
});
});
Upload func
const multer = require('multer');
const path = require('path');
// UPLOAD IMAGE
// Set Storage engine
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: function (req, file, callback) {
callback(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
// Init upload
let upload = multer({
storage: storage,
limits:{fileSize:1000000}, //file size dalam bit
}).fields([{ name: 'fotoDisplay' }, { name: 'newMurid' }]);
With multer, in your req.file, you have all the fields about the file.
var multer = require('multer')
var upload = multer({ dest: 'uploads/' })
import fs from 'fs-extra'
router.post('/register', upload.single('avatar'), (req, res, next) => {
return fs.readFile(req.file.path)
.then(content => {
// The content of the file
})
}
req.file has the mimetype and so much more that you can check

How to resize the image before saving to server for Node JS. (multer itself, gm) I am open to any option

I tried to include image resizing before posting. Multer is used to receiving photos. Then, after using input all information including photos.
I would like to reduce the size and quality of image before they post. However, it's doesn't work. Anyone can giving suggestion?
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'photo')
},
filename: function (req, file, cb) {
cb(null, 'car-' + Date.now() + '.png')
},
})
const upload = multer({ storage: storage })
const gm = require('gm');
module.exports = (app,passport) => {
app.post('/sell', isLoggedIn, upload.any(), (req,res,next) => {
gm(req.files)
.resize(250, 250)
.gravity('Center')
.extent(width, height)
.noProfile()
.write('/photo/' + req.file.fieldname + '-' + Date.now())
async.waterfall([
function(callback) {
var newCar = new Car();
newCar.owner = req.user._id;
newCar.make = req.body.make;
newCar.model = req.body.model;
newCar.year = req.body.year;
newCar.mileage = req.body.mileage;
newCar.price = req.body.price;
newCar.detail = req.body.detail;
newCar.locationProvince = req.body.locationProvince;
newCar.locationDistrict = req.body.locationDistrict;
//newCar.image = req.files;
newCar.save((err) => {
callback(err, newCar);
});
},
function (newCar, callback) {
User.update (
{
_id: req.user._id
},{
$push: {cars: newCar._id }
}, function (err,count) {
req.flash('success', 'success')
res.redirect('/')
}
)
}
]);
});
}
Firstly, please specifies error or something more about your problem. I think you need to console.log -> res.files, it could be an array. Also, check your path in write if it's correct. And the last one, probably you don't add callback function -> write(path, cb).
I can solve it now. But, I don't know how to save the image which have been resized to mongoose.
app.post('/sell', isLoggedIn, upload.any(), (req, res, next) => {
async.waterfall([
function(callback) {
console.log('files', req.files)
if (req.files.length > 0) {
req.files.map(function(file) {
var x = gm(file.path)
.resize(800, 640)
.gravity('Center')
//.extent(250, 250)
.noProfile()
.quality(80)
.write('./photo/resized/' + file.filename +'-800x640', function(err) {
if (err) {
console.log('error : ', err)
}
console.log(file.filename + ' resized!')
});
})
//console.log(req.files.path)
//console.log(req.files)
var newCar = new Car();
newCar.owner = req.user._id;
newCar.make = req.body.make;
newCar.model = req.body.model;
newCar.year = req.body.year;
newCar.mileage = req.body.mileage;
newCar.price = req.body.price;
newCar.detail = req.body.detail;
newCar.locationProvince = req.body.locationProvince;
newCar.locationDistrict = req.body.locationDistrict;
newCar.image = req.files;
newCar.image_resized = x;
newCar.save((err) => {
callback(err, newCar);
});
}
},

Resources