Express Multer validate fields before upload - node.js

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

Related

Multer is not sending error response back when the mimetype is not matched

Here I tried to build an utility function that receives sub folder path, an array of allowed mimetypes, maximum size of the file and the default error message for mimetype. But I am facing a problem when I am trying to upload a file with different mimetype like pdf or something, I can saw the error on the console but it's not sending as a response. But it's sending the response when file is larger than the allowed maximum size of the function.
const path = require('path');
const multer = require('multer');
const createError = require('http-errors');
function uploader(sub_folder, mimetype, max_size, error_msg) {
const upload_path = path.join(__dirname, `../Public/uploads/${sub_folder}`);
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, upload_path);
},
filename: (req, file, cb) => {
const extName = path.extname(file.originalname);
const file_name = file.originalname.toLowerCase().replace(extName, "").split(" ").join("-") + "-" + Date.now() + extName;
cb(null, file_name);
}
});
const upload = multer({
storage: storage,
limits: {
fileSize: max_size,
},
fileFilter: (req, file, cb) => {
if (mimetype.includes(file.mimetype)) {
cb(null, true);
} else {
cb(createError(error_msg));
}
}
});
return upload;
}
module.exports = {
uploader,
}
Here is I called the my uploader utility function and got the multer upload object.
const multer = require('multer');
const {uploader} = require('../../Uitilies/singUpload');
const avatarUploads = (req,res,next) => {
const upload = uploader(
"avater",
["image/png", "image/jpg", "image/jpeg"],
10000000,
"Only .jpg/.jpeg/.png file with 1MB size is allowed"
);
upload.any()(req,res, (err => {
if(err){
console.log(err.message);
res.status(500).json({
errors: {
avatar: {
msg: err.message,
}
}
});
}else{
next();
}
}));
}
module.exports = avatarUploads;
Here I tried to handle the multer error manually. Because I want to handle it myself.
Here I printed the err.message. The message always print on the console but when it's error of a mimetype then the response didn't send. I have also seen that when the err instanceof multer.MulterError is true then the response is sent but when it's false the response did not send.
my router is,
router.post('/', avatarUploads, adduserValidators, adduserValidatorsHandler, addUser);
Any Solution ?????

Expressjs multer middleware save file in dynamic destination

I am using multer with expressjs and I am trying to change the DIR to save images depending on the request to a dynamic destination, my code is working fine but always save images inside the post directory, I'm using this middleware with multi requests.
1- How can I make the directory dynamic! example: to save to ./public/products if req product & save to ./public/posts if req post
2- How to make sure that the file is uploaded to the directory with no errors in the controller! then save the path to the database!
3- Is this the best practice to use multer ! in middleware level!
multer middleware fileUpload.js
const multer = require("multer");
const mkdirp = require("mkdirp");
const fs = require("fs");
const getDirImage = () => {
// const DIR = './public/uploads/products';
return `./public/posts`;
};
let storage = multer.diskStorage({
destination: (req, file, cb) => {
console.log(req.params,'&&&&&&&&&&&&',file);
let DIR = getDirImage();
if (!fs.existsSync(DIR)) {
fs.mkdirSync(DIR, { recursive: true });
}
cb(null, DIR);
},
filename: (req, file, cb) => {
const fileName = "overDress" + Date.now() + "" +
file.originalname.toLowerCase().split(' ').join('-');
cb(null, fileName)
},
});
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 10 //upto 2 megabytes per file.
},
fileFilter: (req, file, cb) => {
if (file.mimetype == "image/png" || file.mimetype == "image/jpg" ||
file.mimetype == "image/jpeg") {
cb(null, true);
} else {
cb(null, false);
return cb(new Error('File types allowed .jpeg, .jpg and .png!'));
}
}
});
module.exports = upload;
product.js route
const controller = require('../controllers/product.controller');
import { Router } from 'express'; //import from esm
import upload from '../middleware/fileUpload'
const router = Router();
router
.get('/', controller.list)
.post('/', upload.single('image'), controller.create)
.get('/:id', controller.getOne)
export default router;
and my create controller:
exports.create = async (req, res, next) => {
const { name, title, description,subtitle} = req.body;
if (!name || !title) {
return res.status(400).send({
message: 'Please provide a title and a name to create a product!',
});
}
try {
if (req.file) {
req.body.image = req.file.destination + '/' + req.file.filename;
}
const PRODUCT_MODEL = {
name: req.body.name,
title: req.body.title,
description: req.body.description,
image: req.body.image,
};
try {
const product = await Product.create(PRODUCT_MODEL);
console.log('product crerated');
return res.status(201).json(product);
} catch (error) {
console.log(error);
return res.status(500).send({
message: 'Something went wrong: ' + error,
});
}
} catch (error) {
return res.status(500).json(error);
}
};

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()

Generate destination path before file upload - multer

Trying to make a folder before uploading a file to it. However, there is a problem if I do not update the server on the node when I try to download the file again, it is added to the already created folder. If I update the server, it creates a new folder and uploads it as needed. Please see the code, how can I improve it?
const db = require('../db');
const fs = require('fs');
const path = require('path');
const uuidv1 = require('uuid/v1');
const multer = require('multer');
console.log(uuidv1())
let storage = multer.diskStorage({
destination: `./uploads/${uuidv1()}`,
filename: (req, file, cb) => {
cb(null, 'test1' + '-' + Date.now() + '.' + path.extname(file.originalname));
}
});
let upload = multer({storage});
module.exports = (router) => {
router.get('/get', (req, res) => {
db.connect.query('SELECT * FROM habalka',
{type: db.sequelize.QueryTypes.SELECT}
).then(result => {
res.json(result);
})
});
router.post('/post', upload.any(), (req, res) => {
res.json('test');
});
return router;
};
Your issue is that when You start Your app it generates new uuid (once - at app startup) and passes as string to diskStorage method.
But You want to generate that path every-time when You upload a file.
So here is the solution:
Multer has feature to dynamically generate both destination path and filename.
So You've to pass a function that will generate path and return it in callback.
Example after reading this manual:
let storage = multer.diskStorage({
// pass function that will generate destination path
destination: (req, file, cb) => {
// initial upload path
let destination = path.join(__dirname, 'uploads'); // ./uploads/
// if user logged in and You store user object in session
if (req.session && req.session.user && req.session.user.id) {
destination = path.join(destination, 'users', req.session.user.id, uuidv1()); // ./uploads/users/8/generated-uuid-here/
}
else {
destination = path.join(destination, 'files', uuidv1()); // ./uploads/files/generated-uuid-here/
}
cb(
null,
destination
);
},
// pass function that may generate unique filename if needed
filename: (req, file, cb) => {
cb(
null,
Date.now() + '.' + path.extname(file.originalname)
);
}
});
My final code is here and it works !Thanks
const db = require('../db');
const fs = require('fs');
const uuid = require('uuid');
const path = require('path');
const multer = require('multer');
const shell = require('shelljs');
console.log(uuid())
let storage = multer.diskStorage({
// pass function that will generate destination path
destination: (req, file, cb) => {
// initial upload path
let destination = path.join(__dirname, '../uploads'); // ./uploads/
let id = uuid();
shell.mkdir('-p', './uploads/' + id);
destination = path.join(destination, '', id); // ./uploads/files/generated-uuid-here/
console.log('dest', destination)
cb(
null,
destination
);
},
// pass function that may generate unique filename if needed
filename: (req, file, cb) => {
let ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
callback(null,file.originalname.split('.').slice(0,-1).join('.') + '-'+Date.now() +ext);
}
});
var upload = multer({storage: storage})
module.exports = (router) => {
router.get('/get', (req, res) => {
db.connect.query('SELECT * FROM habalka',
{type: db.sequelize.QueryTypes.SELECT}
).then(result => {
res.json(result);
})
});
app.post('/uploads', function (req, res) {
upload(req, res, function (err) {
if (err) {
console.log(err);
return res.end("Something went wrong!");
}else{
let ext = req.file.originalname.substring(req.file.originalname.lastIndexOf('.'), req.file.originalname.length);
var files='./uploads/'+req.file.originalname.split('.').slice(0 ,-1).join('.')+'-'+Date.now()+ext
console.log(req.file.originalname);
collection.insertOne({files},function(err,result){
if(err){
console.log("Something is Wrong");
}else{
res.json(result._id);
console.log(result);
}
})
}
});
})
return router;
};
In your multer middleware, you can do something like:
import util from "util";
import multer from "multer";
export const maxSize = 20 * 1024 * 1024;
export const __upoads_folder='/volume1/server/dash_rental_server/uploads';
let uploadFile = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => {
const fileName = req.params.name;
let directoryPath = __upoads_folder + "/";
if (req?.params?.folder) { directoryPath += req.params.folder + '/' };
if (req?.params?.category) { directoryPath += req?.params?.category + '/' };
cb(null, directoryPath);
},
filename: (req, file, cb) => {
console.log(file.originalname);
cb(null, file.originalname);
},
}),
limits: { fileSize: maxSize },
}).single("file");
export let uploadFileMiddleware = util.promisify(uploadFile);
Then, in your route:
router.post("/upload/:folder/:category", async (req, res) => {
try {
await uploadFileMiddleware(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 ${maxSize / 1024 / 1024} MB!`,
});
}
res.status(500).send({
message: `Could not upload the file: ${req.file.originalname}. ${err}`,
});
}
});

Resources