Expressjs multer middleware save file in dynamic destination - node.js

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);
}
};

Related

NodeJS / Multer : How to replace exising file in directory with new file

I want to replace existing file in directory when upload new new , I'm using nodeJS and multer, the idea is to read directory content, delete it and then upload the new file.
const express = require('express')
const router = express.Router()
const authorize = require('_middleware/authorize')
const Role = require('_helpers/role')
const uploadImage = require("./uploadAdvertising")
const fs = require('fs')
const uploadDir = '/public/advertising/'
// routes
router.post('/', authorize(Role.Admin), uploadImage, createAdvertising);
module.exports = router;
async function createAdvertising(req, res, next) {
fs.readdir(global.__basedir + uploadDir, ((err, files) => {
if (err) throw err
for (const file of files){
fs.unlink(global.__basedir + uploadDir + file, err => {
if (err) throw err;
});
}
}))
if (!req.file) return res.status(412).send({
message: `image is required`,
});
uploadImage(req, res)
.then(data => {
return res.status(201).send({
message: 'Advertising image saved'
})
}).catch(err => {
return res.status(500).send({
message: `Could not upload the file, ${err}`,
});
})
}
multer
const util = require("util");
const multer = require("multer");
const maxSize = 10 * 1024 * 1024;
const uploadDir = '/public/advertising/'
const fs = require('fs')
let storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, global.__basedir + uploadDir)
req.uploadDirectory = global.__basedir + uploadDir
},
filename: (req, file, cb) => {
cb(null, file.originalname)
},
});
let uploadImage = multer({
storage: storage,
fileFilter: (req, file, cb) => {
//reject
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true)
} else {
cb(new Error("image should be png or jpeg extension"), false)
}
},
limits: {fileSize: maxSize},
}).single("imageAdvertising");
let uploadFileMiddleware = util.promisify(uploadImage);
module.exports = uploadFileMiddleware;
can some one take a look and tell what's wrong with this code ! it delete all content even the new uploaded file !
just try like this in the multer file
note : write the path of uploaded file in unlinkSync and readdirSync
fileFilter: (req, file, cb) => {
let files = fs.readdirSync('write the path');
if(files.includes(file.originalname)){
fs.unlinkSync('pwrite the pathath'+ file.originalname);
}
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true)
} else {
cb(new Error("image should be png or jpeg extension"), false)
}
}
and remove extra code like createAdvertising file

Handle destination per route in nodejs with multer package

I use multer package and make it dynamic and its work. Now it try to make it dynamic per route and stuck with this.
This is my fileupload service:
const path = require("path");
const multer = require("multer");
const crypto = require("crypto");
const fs = require("fs-extra");
const today = new Date();
const date = today.getFullYear() + "-" + (today.getMonth() + 1) + "-" + today.getDate();
const storage = multer.diskStorage({
destination: (req, file, callback) => {
const { companyID, flowID } = req.body;
let path = `./uploads/${companyID}/${flowID}`;
fs.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)
);
});
}
});
//Must be under storage
const upload = multer({ storage: storage }).single("upload");
const deleteFileFromServer = (companyID, flowID, filePath) => {
return new Promise((resolve,reject)=>{
fs.unlink(`./uploads/${companyID}/${flowID}/${filePath}`).then(()=>{
resolve("File Deleted");
}).catch((err)=> {
reject(err);
})
})
};
module.exports = {
upload: upload,
deleteFileFromServer: deleteFileFromServer
};
my route: Here i want handle destination in every route set diffrent destination
router.post("/uploadfile", upload, (req, res, next) => {
const file = req.file;
const flowID = req.body.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);
});
}
});
What i want it to be? i want that every route i can choose different destination.
router.post("/uploadfile", upload, (req, res, next) => {
upload.destination = "my path/"
});
Thanks!!

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}`,
});
}
});

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

Resources