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})
}
Related
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",
});
}
}
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;
iam trying to upload image in s3 this is my code
const upload = require('../services/file_upload');
const singleUpload = upload.single('image');
module.exports.uploadImage = (req,res) => {
singleUpload(req, res, function (err) {
if (err) {
console.log(err);
return res.status(401).send({ errors: [{ title: 'File Upload Error', detail: err}] });
}
console.log(res);
return res.json({ 'imageUrl': req.file.location });
});
}
FileUpload.js
const aws = require('aws-sdk');
const multer = require('multer');
const multerS3 = require('multer-s3');
const s3 = new aws.S3();
const fileFilter = (req, file, cb) => {
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true)
} else {
cb(new Error('Invalid Mime Type, only JPEG and PNG'), false);
}
}
const upload = multer({
fileFilter,
storage: multerS3({
s3,
bucket: 'image-bucket',
acl: 'public-read',
contentType: multerS3.AUTO_CONTENT_TYPE,
metadata: function (req, file, cb) {
cb(null, {fieldName: 'TESTING_META_DATA!'});
},
key: function (req, file, cb) {
cb(null,"category_"+Date.now().toString()+".png")
}
})
})
module.exports = upload;
I tried to test api with postmanin serverless local it is giving this error
Error: Unexpected end of multipart data
at D:\Flutter\aws\mishpix_web\node_modules\dicer\lib\Dicer.js:62:28
at process._tickCallback (internal/process/next_tick.js:61:11) storageErrors: []
After deploying online. i tried the api. the file is uploaded in server but its a broken
Are you using aws-serverless-express. aws-serverless-express will forward original request to Buffer as utf8 encoding. So multipart data will be lost or error. I am not sure why.
So, I change aws-serverless-express to aws-serverless-express-binary and everything worked.
yarn add aws-serverless-express-binary
Hope this help!
I am using multer's array method as a middleware for my post route and I'm trying to figure out how I can send the error callback from the fileFilter function in the multer options setup as a flash message when the group of files that a user is uploading contains at least one file with the wrong format. My current fileFilter setup achieves this, but instead of sending the user to a blank page with the File Selected is not supported message, I was looking for a way to communicate that to the route using multer as middleware.
Here is my multer setup:
var upload = multer({
storage: multerS3({
s3: s3,
bucket: options.Bucket,
contentType: multerS3.AUTO_CONTENT_TYPE,
acl: options.ACL,
key: function(req, file, cb){
var fileNameFormatted = file.originalname.replace(/\s+/g, '-').toLowerCase();
cb(null, req.user.organizationId + '/' + uploadDate + '/' + fileNameFormatted);
}
}),
fileFilter: function(req, file, cb){
if(!file.originalname.match(/\.(jpg|jpeg|png|gif|csv|xls|xlsb|xlsm|xlsx)$/)){
return cb('File Selected is not supported');
}
cb(null, true);
}
});
Here is my route with upload.array('fileUpload', 5) fileUpload is the name of my file input field and 5 is the multer file length limiting feature.
.post(upload.array('fileUpload', 5), function(req, res) {
//Configure Uploaded S3 File Path strings based on environment for use in DB
var uploadedFiles = req.files;
var s3FilePath = [];
for (var prop in uploadedFiles) {
console.log(uploadedFiles[prop].key);
if (app.get('env') === 'production' || app.get('env') === 'staging') {
s3FilePath = 'https://files.test-site.com/' + uploadedFiles[prop].key;
} else {
s3FilePath.push(uploadedFiles[prop].location);
}
}
models.Blog.update({
blogId: req.body.blogId,
blogDate: req.body.blogDate,
}, {
where: {
userId: req.user.userId,
blogId: req.body.blogId
}
}).then(function(blog) {
console.log('This is the blog ' + blog);
var files = _.map(s3FilePath, function(file) {
console.log(file);
return {
fileName: file,
blogId: req.body.blogId
};
});
return models.BlogFile.bulkCreate(files);
}).then(function() {
res.redirect('/app');
}).catch(function(err) {
res.send(err);
console.log('File Post Error ' + err);
});
});
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.