Change multer destination folder based on the request - express

I am using express Multer middleware to save images in my application, I am using the same middleware to save the users, posts, and products images, the problem is all the images saved to the same directory "./public/uploads/", and what I want is to save each request to a specific folder,
For example:
The posts images to "./public/uploads/posts"
The products images to "./public/uploads/products"
I couldn't find a way to pass a variable from the routes to the middleware to change the destination dynamically
Could you please assist with this?
thanks in advance.
Here is my code:
Multer middleware file (multer.js)
const multer = require("multer");
const fs = require("fs");
let configDIR = "`./public/uploads/";
let storage = multer.diskStorage({
destination: (req, file, cb) => {
console.log('req.query.name', req.query.name)
let DIR = configDIR;
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 //up to 1 megabytes per file.
},
}).single("image");
module.exports = {
upload,
};
One of my routes, the post route:
const controller = require('../controllers/post.controller');
import { Router } from 'express';
const router = Router();
import {upload} from '../middleware/multer'
let ImageFolder = 'posts'
router
.post('/',
(req, res, next) => {
upload(req, res, (err) => {
if (err)
return res.send({status:false, message: 'Invalid Image', error: err })
console.log('File Saved with no errors')
next()
}
)
export default router;

Related

How do I make express middleware in class?

I currently use multer middleware like below
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public");
},
filename: function (req, file, cb) {
cb(null, req.params.id + "_" + file.originalname);
},
});
export const multerUploadSingle = (req: Request, res: Response, next: NextFunction) => {
const upload = multer({ storage: storage }).single("file");
upload(req, res, (error: unknown) => {
if (error instanceof multer.MulterError) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.BadRequest));
} else if (error instanceof Error) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.InternalServerError));
} else {
// upload success
next();
}
});
}
and use in router like this
FileRouter.post("/upload/:id", multerUploadSingle, (req, res) => {...});
However, I felt I want to refactor this middleware in class, and rewrote the code like this,
export class Multer {
private readonly storage: multer.StorageEngine;
constructor() {
this.storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public");
},
filename: function (req, file, cb) {
cb(null, req.params.id + "_" + file.originalname);
},
});
}
uploadSingle(req: Request, res: Response, next: NextFunction) {
const upload = multer({ storage: this.storage }).single("file");
upload(req, res, (error: unknown) => {
if (error instanceof multer.MulterError) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.BadRequest));
} else if (error instanceof Error) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.InternalServerError));
} else {
// upload success
next();
}
});
}
}
const multer = new Multer();
FileRouter.post("/upload/:id", multer.uploadSingle, (req, res) => {...});
With my short knowledge, I think both case should have the same result, but the latter case which uses class made middleware doesn't work at all. It's seems method "uploadSingle" is never called, thus multer not uploading the file.
Did I make any mistake with class usage? or is it just express can only use function defined middleware?
Your code should follow the MVC pattern.
You can do stuff like this:
routerFile.js
const upload = require("../../configs/multer");
const postController = require("../../controllers/postController");
const multiUploadEvent = upload.fields([
{ name: "images", maxCount: 2 },
{ name: "video", maxCount: 2 }
]);
router.post("/add-event-post", multiUploadEvent, postController.addEventPost);
module.exports = router;
multer.js
const multer = require('multer');
const multerFilter = (req, file, cb) => {
console.log("Mime type :", file.mimetype.split('/')[0]);
if (file.mimetype.split('/')[0] === 'image' || file.mimetype.split('/')[0] === 'video' || file.mimetype.split('/')[0] === 'audio') {
cb(null, true);
} else {
cb(new Error('Please upload img, audio, or video file only.'), false);
}
};
const storage = multer.memoryStorage();
const upload = multer({
storage: storage,
fileFilter: multerFilter,
limits: {
fileSize: , 50 * 1024 * 1024// 50 Mb
},
});
module.exports = upload;
postController.js
const addEventPost = async (request, response) => {
try {
let { title, ..... } = request.body;
const images = request.files.images;
const video = request.files.video;
console.log(title);
console.log(images);
console.log(videos);
//upload to services likes aws and save to database
.
.
.
return response
.status(200)
.json({
message: "Event post added successfully"
});
} catch (error) {
console.log(error);
response.status(500).json({
error: "Something went wrong",
});
}
}

Express js- how to send full path to client with multer

I use multer to save the names of the pictures in separate folders in storageā€¤ I want to send the client not only the name of the file, but also connect the path to the folder to it. For example: {icon: 'storage/categories/name'}, how to dynamically set it up? My middleware is this:
const multer = require('multer');
module.exports = function (directory){
const storage = multer.diskStorage({
destination(req, file, cb){
cb(null, `storage/${directory}`)
},
filename(req, file, cb){
cb(null, new Date().toISOString().replace(/:/g, '-') + file.originalname)
}
})
const types = ['image/png', 'image/jpeg', 'image/jpg']
const fileFilter = (req, file, cb) => {
if (types.includes(file.mimetype)) {
cb(null, true)
} else {
cb(null, false)
}
}
return multer({storage, fileFilter})
}

multer file upload - how to get a value from multer in route?

I'm uploading a file using multer with Express.
I'd like access a value from multer's storage object inside the route.
How can I do that?
Multer configuration (right now I only know how to log the key):
const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");
function configureUpload () {
const s3 = new aws.S3({...my credentials...});
const upload = multer({
storage: multerS3({
s3: s3,
bucket: process.env.S3_BUCKET_NAME,
metadata: (req, file, cb) => cb(null, { fieldName: file.fieldname }),
key: (req, file, cb) => {
const key = `${new Date().toISOString()}-${file.originalname}`;
return cb(console.log("KEY: ", key), key); // The string I need to access in route
},
}),
});
return upload;
}
The route:
const express = require("express");
const Person = require("../../../db/models/person");
const configureUpload = require("../../../configureUpload ");
const router = express.Router();
// Saving to MongoDB with mongoose
router.post("/", configureUpload ().any(), async (req, res) => {
Person.create({
...req.body,
files: [] // I want to add the string in multer.storage.key to this array
})
.then((person) => {
...
})
.catch((err) => {
...
});
});
module.exports = router;
This an exapmle of what Tarique Akhtar Ansari already said. Adding your key to the req object so that you can access the key's value in your controller/route like so:
const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");
function configureUpload () {
const s3 = new aws.S3({...my credentials...});
const upload = multer({
storage: multerS3({
s3: s3,
bucket: process.env.S3_BUCKET_NAME,
metadata: (req, file, cb) => {cb(null, { fieldName: file.fieldname })},
key: (req, file, cb) => {
const key = `${new Date().toISOString()}-${file.originalname}`;
req.key = key; // added the key to req object
// return cb(console.log("KEY: ", key), key); // The string I need to access in route
},
}),
});
return upload;
}
Accessing the value of the key inside your controller or route
const express = require("express");
const Person = require("../../../db/models/person");
const configureUpload = require("../../../configureUpload ");
const router = express.Router();
// Saving to MongoDB with mongoose
router.post("/", configureUpload ().any(), async (req, res) => {
console.log('here is the value your key', req.key); // it's that simple.
Person.create({
...req.body,
files: [] // I want to add the string in multer.storage.key to this array
})
.then((person) => {
...
})
.catch((err) => {
...
});
});
module.exports = router;
you can simply add req.key = keyValue
then you can access in next route using req.key name
or you can also access req.file or req.files object in route
In express everything is a middleware so you can easily pass and access in next middleware

Multer isn't passing in express put route

I'm trying to upload an image to the file system with Multer. Please take a look at the relevant data in my route:
const
..
multer = require('multer'),
..;
const storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './uploads');
},
filename: function (req, file, callback) {
callback(null, req.params.id + file.originalname);
}
}),
upload = multer({storage: storage}).single('profilePic');
router.put(
'/:id',
middleware.isLoggedIn,
(req, res, next) => {
User
.findByIdAndUpdate(
req.params.id, req.body.user,
(err, updatedUser) => {
if (err) {
return req.flash('error', err.message);
}
upload(req, res, (err) => {
if (err) {
eval(locus);
return req.flash('error', err.message);
}
updatedUser = req.body.user;
eval(locus);
//redirect show page
res.redirect('/dashboard/profile/' + req.params.id + '/edit');
});
});
});
module.exports = router;
When I look at updatedUser the first thing I see is
{ profilePic: 'data:image/jpeg;base64,....} what am I doing wrong? It's not even updating the page now that I have the upload function in here. What I really want to do is get the destination to work on s3 but I need to get this to save first.
So, this is a the most basic example of uploading an image using multer:
var express = require('express')
var multer = require('multer')
var app = express()
var storage = multer.diskStorage({
// define where the file should be uploaded, else it will be uploaded to the system temp dir
destination: function (req, file, cb) {
// ./uploads should be created beforehand
cb(null, './uploads')
},
// define "filename", else a random name will be used for the uploaded file
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + file.originalname)
}
})
var upload = multer({ storage: storage })
// pic is the name of image field in the form
app.put('/profile', upload.single('pic'), function (req, res, next) {
console.log(req.file)
res.send('Uploaded')
})
app.listen(3000)
And here is an example curl command to upload an image from the file system to the above app:
curl -X PUT -F 'pic=#/projects/eg/foto.png' localhost:3000/profile
Make sure the example works fine, to ensure you understand how multer handles file uploads, and that the issue is not with multer.
That said and done, User.findByIdAndUpdate seems to be storing the image data as a base64 encoded string somewhere; I have no idea what User.findByIdAndUpdate connects to. It is beyond the domain of multer.
Someone on our Gitter channel (https://gitter.im/expressjs/express) might be able to suggest something. Join us there.

Where should I put the middleware if a sub router module used in Express.js

All:
When I learn how to upload file with Express.js, there is a middleware called multer ( https://github.com/expressjs/multer ), and from its Usage example:
var express = require('express')
var multer = require('multer')
var upload = multer({ dest: 'uploads/' })
var app = express()
app.post('/profile', upload.single('avatar'), function (req, res, next) {
// req.file is the `avatar` file
// req.body will hold the text fields, if there were any
})
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
// req.files is array of `photos` files
// req.body will contain the text fields, if there were any
})
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {
// req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
//
// e.g.
// req.files['avatar'][0] -> File
// req.files['gallery'] -> Array
//
// req.body will contain the text fields, if there were any
})
The middleware object directly used in app.js, I wonder if I use sub router like:
app.js
var routes = require('./routes/index');
app.use("/", routes);
index.js
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
router.post('/upload', function(req, res, next) {
debugger;
console.log(req.body.upld);
console.log(req.file);
res.send("");
});
module.exports = router;
I wonder where should I put that upload middleware? Should I put that var upload = multer({ dest: 'uploads/' }) in each file use it? If so, will this cause several uploads folders generated in different folders based on the router file located?
Thanks