File upload makes Node JS unresponsive with Multer - javascript

I am using Multer Node JS package to upload files to my app sever , the code is basically typical upload file code
const express = require('express')
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })
const 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
})
But each time a file is being uploaded the Node server becomes unresponsive and frontend from other request doesnt receive any response from other APIs until the file is uploaded.
Whats the best way to tackle this ?

In your sample code, you must just send a response to the client by res.json() or res.end() :
const express = require('express');
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
const 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
res.status(204).end();
});
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
res.status(204).end();
});

i can give you an example of how i implemented an imageupload in my app. it the code to upload a profile image for a user. i am also using multer middleware so it shoulder be similiar for you:
code is as follows:
// multer middleware:
const multer = require('multer');
const MIME_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpg',
'image/jpg': 'jpg',
};
module.exports = storage = multer.diskStorage({
destination: (req, file, cb) => {
const isValid = MIME_TYPE_MAP[file.mimetype];
let error = new Error('invalid mime type');
if (isValid) {
error = null;
}
cb(error, 'images');
},
filename: (req, file, cb) => {
const name = file.originalname.toLowerCase().split(' ').join('-');
const ext = MIME_TYPE_MAP[file.mimetype];
if (name.includes('.' + ext)) {
cb(null, name)
} else {
cb(null, name + '.' + ext);
}
},
});
and here the code in the service handling the fileupload
// profile service in backend written in express
exports.uploadImage = (req, res, next) => {
const url = req.protocol + '://' + req.get('host');
profileRepository
.findOne({ _id: req.params.id })
.then((response) => {
const fetchedUser = response;
fetchedUser.imagePath = url + '/images/' + req.file.filename;
profileRepository
.updateOne({ _id: req.params.id }, fetchedUser)
.then((response) => {
return res.status(200).json({
message: 'profileimage updated',
});
})
.catch((error) => {
return res.status(500).json({
message: 'uploading image failed',
});
});
})
.catch((error) => {
return res.status(404).json({
message: 'fetching user failed',
});
});
};
then i use the middleware in my profile routes file like this:
// profile.routes.js
const express = require('express');
const ProfileController = require('./profileController');
const checkAuth = require('../middleware/checkAuth');
const router = express.Router();
const fileStorage = require('../middleware/fileStorage');
const multer = require('multer');
// imageUpload
router.post('/user/image/:id', checkAuth, multer({storage: fileStorage}).single('image'), ProfileController.image);
my Controller then calls the service function with the actual business logic like this:
// profile.controller.js
const profileService = require('./profileService');
exports.image = (req, res, next) => {
return profileService.uploadImage(req, res);
};
and finally my route is used by my app.js file like this:
// app.js
const express = require('express');
const profileRoutes = require('./profile/profileRoutes');
const app = express();
// set images path for saving images on server
app.use('/images', express.static(path.join('images')));
app.use('/api', profileRoutes);
module.exports = app;
i hope i was able to point you in the right direction with my example

Related

why req.body.title shows 'undefined'

I'm new to NODE JS and practicing with some POST forms from PUG to a NODE JS server.
I have a simple form to update a photo title and description posted onto mongodb. When I submit the form from the web browser the submission input comes back to the server as 'undefined'.
These two processes in POST log 'undefined': (see below with more full code)
console.log("title:", req.body.title)
console.log("description", req.body.description)
I've tried to use PUT instead. Weirdly I've done this before and it's worked. So I'm not sure what the issue is...
router handling the POST request:
//users.js
const express = require('express');
const router = express.Router();
const app = express();
const multer = require('multer');
const photoController = require('../controllers/photoController');
const flash = require('express-flash');
const Photo = require('../models/photoModel');
const upload = multer({
storage: photoController.storage,
fileFilter: photoController.imageFilter
});
// flash messaging
router.use(flash());
router.get('/', (req, res, next)=>{
Photo.find({})
.then((photos)=>{
res.render('photos', {
photos : photos,
flashMsg: req.flash("fileUploadError")
});
})
.catch((err)=>{
if (err) {
res.end("ERROR!");
}
});
});
router.get('/:photoid', (req, res, next)=>{
console.log("finding "+req.params.photoid);
Photo.findOne({'_id': req.params.photoid})
.then((photo)=>{
res.render('updatePhoto', {
photo: photo,
flashMsg: req.flash("photoFindError")
});
}).catch((err)=>{
if (err) console.log(err);
});
});
// I think the error is below!!
router.post('/:photoid', (req, res, next)=>{
console.log("title:", req.body.title)
console.log("description", req.body.description)
Photo.findOne({'_id': req.params.photoid})
.then((photo)=>{
var data = {
title: req.body.title,
description: req.body.description
}
photo.set(data);
photo.save().then(()=>{
res.redirect('/photos');
});
})
.catch((err)=>{
if (err) console.log(err);
});
});
PUG form:
.row
.col-md-6.col-md-offset-3
if flashMsg.length > 0
.alert.alert-danger <strong>FLASH!</strong>#{flashMsg}
p Title: #{photo.title}
p Description: #{photo.description}
p Size: #{photo.size} | Filename: #{photo.originalname} | Uploaded: #{photo.createdAt}| Modified: #{photo.updatedAt}
img(src=photo.imageurl, width="250")
form(method='POST' action="/photos/"+photo._id enctype="multipart/form-data")
div.form-group
label(for='name') Photo Title :
input#name.form-control(type='text', value=photo.title name='title')
div.form-group
label(for='email') Description:
input#email.form-control(type='text', value=photo.description name='description')
div.form-group
label(for='image') Image:
input#name.form-control(type='hidden', name='_id' value=photo._id)
button.btn.btn-primary(type='submit') Update Your Photo
Thanks for your help
You should add to your code the app.use(express.json()) middleware in order to parse automatically the req.body from your request.
const express = require('express');
const router = express.Router();
const app = express();
app.use(express.json()); // Here
const multer = require('multer');
const photoController = require('../controllers/photoController');
const flash = require('express-flash');
const Photo = require('../models/photoModel');
const upload = multer({
storage: photoController.storage,
fileFilter: photoController.imageFilter
});

Cannot POST /images | MongoDB y Mongoose

I am developing a project with a friend in NodeJS and we are using express, Mongoose and when uploading images to the server it throws us this error: Cannot POST / images Here I leave the code in case someone can help me please:
const fs = ('fs-extra');
const path = ('path');
const md5 = ('md5');
const ctrl = {};
const Image = require('../models/image.js');
ctrl.create = (req, res) => {
const saveImage = async () => {
const imgUrl = randomNumber();
const images = await Image.find({ filename : imgUrl});
if(images.length > 0) {
saveImage()
} else {
const imageTempPath = req.file.path;
const ext = path.extname(req.file.originalname).toLowerCase();
const targetPath = path.resolve('/src/public/upload/${imgUrl}${ext}');
if(ext == '.png' || ext == '.jpg' || ext == '.gif' || ext == '.jpeg') {
await fs.rename(imageTempPath, targetPath);
const newImg = new Image({
filename: imgUrl + ext
});
const imageSaved = await newImg.save();
res.redirect('/images/' + imageSaved.uniqueId);
} else {
await fs.unlink(imageTempPath);
res.status(500).json({ error: 'Solo se permiten Imagenes'})
}
}
};
saveImage();
};
module.export = ctrl;
This is the controller that I have for uploading images and this is the model:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const path = require('path');
const ImageSchema = new Schema({
filename: { type: String }
});
ImageSchema.virtual('uniqueId')
.get(function () {
return this.filename.replace(path.extname(this.filename), '');
});
module.exports = mongoose.model('Image', ImageSchema);
And finally this is the route I use for uploading images (in addition to having some routes such as login and user registration):
const router = require('express').Router();
const passport = require('passport');
const multer = require('multer');
const path = require('path');
const fs = require('fs-extra');
const image = require('../controllers/image');
module.exports = app => {
router.post('/images', image.create);
}
router.get('/', (req, res, next) => {
res.render('index');
});
router.get('/signup', (req, res, next) => {
res.render('signup');
});
router.post('/signup', passport.authenticate('local-signup', {
successRedirect: '/profile',
failureRedirect: '/signup',
failureFlash: true
}));
router.get('/signin', (req, res, next) => {
res.render('signin');
});
router.post('/signin', passport.authenticate('local-signin', {
successRedirect: '/profile',
failureRedirect: '/signin',
failureFlash: true
}));
module.exports = router;
router.use((req, res, next) => {
isAuthenticated(req, res, next);
next();
});
router.get('/profile', (req, res, next) => {
res.render('profile');
});
router.get('/logout', (req, res, next) => {
req.logout();
res.redirect('/');
});
function isAuthenticated(req, res, next) {
if(req.isAuthenticated()) {
return next();
}
res.redirect('/')
}
I would appreciate it very much if you could help me
Thank you.
You need to use multer to save images in MongoDB according to THIS article.
The important takeaway here is that our data type is a Buffer, which allows us to store our image as data in the form of arrays.
const multer = require('multer');
mongoose.connect(‘url_here’);
const Item = new ItemSchema(
{ img:
{ data: Buffer, contentType: String }
}
);
const Item = mongoose.model('Clothes',ItemSchema);
app.use(multer({ dest: ‘./uploads/’,
rename: function (fieldname, filename) {
return filename;
},
}));
app.post(‘/api/photo’,function(req,res){
var newItem = new Item();
newItem.img.data = fs.readFileSync(req.files.userPhoto.path)
newItem.img.contentType = ‘image/png’;
newItem.save();
});
Or follow this post.
Store an image in MongoDB using Node.js/Express and Mongoose

Multer destination file is not readable by ZEIT now

Hi im triying to upload images to cloudinary using multer in my nodejs app it works perfectly on the localhost but when i upload it to zeit it doesn't work it says "ENOENT: no such file or directory, open (folder)"
I tried using #now/static to make the folder available but it doesn't work too. I will be happy if you can help me.
const express = require("express")
const multer = require("multer")
const cloudinary = require("cloudinary").v2
const cors = require("cors")
const config = require("../../config")
const response = require("../../network/response")
const Controller = require("./index")
const auth = require("./secure")
const router = express.Router()
cloudinary.config({
cloud_name: config.cloudinary.name,
api_key: config.cloudinary.api_key,
api_secret: config.cloudinary.api_secret,
})
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./public/uploads")
},
filename: function (req, file, cb) {
cb(null, file.originalname)
},
})
const upload = multer({
storage: storage,
limits: { fileSize: 1024 * 1024 * 5 },
})
// Set routes
router.use(cors())
router.get("/", auth("list"), list)
router.post("/", auth("add"), upload.single("file"), upsert)
// Router Functions
function list(req, res, next) {
Controller.list()
.then((post) => {
response.success(req, res, post, 200)
})
.catch(next)
}
function upsert(req, res, next) {
const path = req.file.path
const uniqueFilename = new Date().toISOString()
cloudinary.uploader.upload(
path,
{ public_id: `public/${uniqueFilename}` }, // directory and tags are optional
function (err, image) {
if (err) return res.send(err)
console.log("file uploaded to Cloudinary")
// remove file from server
const fs = require("fs")
fs.unlinkSync(path)
}
)
Controller.upsert(req.body, uniqueFilename)
.then((post) => {
response.success(req, res, post, 201)
})
.catch(next)
}
module.exports = router
When working with Zeit/Vercel, your API code runs in a Lambda.
This lambda is meant to be disposable when it finishes executing,
therefore the filesystem that you use in your API is destroyed upon the end of your upload execution.
(it works locally because no one destroys your computer at the end :D).
To get this scenario working, you'll need to write the uploaded file somewhere persistent, such as AWS S3.

Image Not Uploading to Google Cloud Storage with Multer?

EDIT: Figured out the issue, solution below.
I'm using multer and multer google storage to try to upload image files to my Google Cloud Bucket, but for some reason the files aren't being saved into the Google Cloud Bucket, and I can't log any errors, not sure what I'm doing wrong here. (Have tried to follow several different tutorials, read documentation, check other SO questions, etc. Still no solution).
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const passport = require('passport');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const multer = require('multer');
const path = require('path');
const multerGoogleStorage = require('multer-google-storage');
const { Storage } = require('#google-cloud/storage');
const gc = new Storage({
projectId: '{projectIdRedacted}',
keyFilename: path.join(__dirname, '../{keyFileNameRedacted.json}')
});
gc.getBuckets().then(x => console.log(x));
// This is showing that I've successfully paired to the Google Cloud bucket.
const bucket = gc.bucket('{redactedBucketNameHere}');
const fileFilter = (req, file, cb) => {
// Reject a file
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(null, false);
}
};
var uploadHandler = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 1024 * 1024 * 1
},
fileFilter: fileFilter
});
// Testing GCP Bucket Image Upload
// #route POST image-upload
// #desc Add image
// #access Private
router.post('/image-upload', uploadHandler.single('UploadBox'), passport.authenticate('jwt', {
session: false
}), (req, res, next) => {
// This is showing the req.file is being passed through
console.log(req.file);
const blob = bucket.file(req.file.originalname);
const blobStream = blob.createWriteStream({
metadata: {
contentType: req.file.mimetype
},
resumable: false
});
// The err is not getting console logged even though it is not saving to the google cloud bucket properly?
blobStream.on('error', err => {
next(err);
console.log(err);
return;
})
// The publicUrl is not getting console.logged - presumably cause something is breaking before this and it won't save it
blobStream.on('finish', () => {
// the public url can be used to directly access the file via HTTP
const publicUrl = `https://storage.googleapis.com/${bucket.name}/${blob.name}`;
console.log(publicUrl);
// Make the image public to the web (since we'll be displaying it in the browser)
blob.makePublic().then(() => {
res.status(200).send(`Success!\n Image uploaded to ${publicUrl}`);
})
})
});
The documentation for #google-cloud/storage is: https://www.npmjs.com/package/#google-cloud/storage
The documentation for multer google storage is: https://www.npmjs.com/package/multer-google-storage
The documentation for Google's guide on using their cloud storage is: https://cloud.google.com/appengine/docs/flexible/nodejs/using-cloud-storage
Any tips and help would be greatly appreciated.
EDIT: I figured out the solution. I had to move up the uploadHandler and the fileFilter ABOVE the const { storage} import. And then inside the route, I had to add "blobStream.end();" after the blobStream.on('finish'). After doing so it resolved it. I've edited the working code below.
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const passport = require('passport');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const multer = require('multer');
const path = require('path');
const multerGoogleStorage = require('multer-google-storage');
const fileFilter = (req, file, cb) => {
// Reject a file
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(null, false);
}
};
var uploadHandler = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 1024 * 1024 * 1
},
fileFilter: fileFilter
});
const { Storage } = require('#google-cloud/storage');
const gc = new Storage({
projectId: '{projectIdRedacted}',
keyFilename: path.join(__dirname, '../{keyFileNameRedacted.json}')
});
gc.getBuckets().then(x => console.log(x));
const bucket = gc.bucket('{bucketNameRedacted}');
// Testing GCP Bucket Image Upload
// #route POST image-upload
// #desc Add image
// #access Private
router.post('/image-upload', uploadHandler.single('UploadBox'), passport.authenticate('jwt', {
session: false
}), (req, res, next) => {
// This is showing the req.file is being passed through
console.log(req.file);
const blob = bucket.file(req.file.originalname);
const blobStream = blob.createWriteStream({
metadata: {
contentType: req.file.mimetype
},
resumable: false
});
// The err is not getting console logged even though it is not saving to the google cloud bucket properly?
blobStream.on('error', err => {
next(err);
console.log(err);
return;
})
// The publicUrl is not getting console.logged - presumably cause something is breaking before this and it won't save it
blobStream.on('finish', () => {
// the public url can be used to directly access the file via HTTP
const publicUrl = `https://storage.googleapis.com/${bucket.name}/${blob.name}`;
console.log(publicUrl);
// Make the image public to the web (since we'll be displaying it in the browser)
blob.makePublic().then(() => {
res.status(200).send(`Success!\n Image uploaded to ${publicUrl}`);
})
})
blobStream.end();
});
You don't really need the multer-google-storage package, that's by the way.
kindly comment the response error message you get when you hit the route meant for this upload.
I figured out the solution. I had to move up the uploadHandler and the fileFilter ABOVE the const { storage} import. And then inside the route, I had to add "blobStream.end();" after the blobStream.on('finish'). After doing so it resolved it. I've edited the working code below.
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const passport = require('passport');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const multer = require('multer');
const path = require('path');
const multerGoogleStorage = require('multer-google-storage');
const fileFilter = (req, file, cb) => {
// Reject a file
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(null, false);
}
};
var uploadHandler = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 1024 * 1024 * 1
},
fileFilter: fileFilter
});
const { Storage } = require('#google-cloud/storage');
const gc = new Storage({
projectId: '{projectIdRedacted}',
keyFilename: path.join(__dirname, '../{keyFileNameRedacted.json}')
});
gc.getBuckets().then(x => console.log(x));
const bucket = gc.bucket('{bucketNameRedacted}');
// Testing GCP Bucket Image Upload
// #route POST image-upload
// #desc Add image
// #access Private
router.post('/image-upload', uploadHandler.single('UploadBox'), passport.authenticate('jwt', {
session: false
}), (req, res, next) => {
// This is showing the req.file is being passed through
console.log(req.file);
const blob = bucket.file(req.file.originalname);
const blobStream = blob.createWriteStream({
metadata: {
contentType: req.file.mimetype
},
resumable: false
});
// The err is not getting console logged even though it is not saving to the google cloud bucket properly?
blobStream.on('error', err => {
next(err);
console.log(err);
return;
})
// The publicUrl is not getting console.logged - presumably cause something is breaking before this and it won't save it
blobStream.on('finish', () => {
// the public url can be used to directly access the file via HTTP
const publicUrl = `https://storage.googleapis.com/${bucket.name}/${blob.name}`;
console.log(publicUrl);
// Make the image public to the web (since we'll be displaying it in the browser)
blob.makePublic().then(() => {
res.status(200).send(`Success!\n Image uploaded to ${publicUrl}`);
})
})
blobStream.end();
});

Express middleware (multer) in seperate file

I was able to move a very simple middleware (isAuthenticated) over to an external middleware file, but I'm having a harder time with moving my multer upload over. I just learned how to move them to seperate files, so it's probably obvius.
routes/index.js
var middleware = require('../middleware/common');
var isAuthenticated = middleware.isAuthenticated;
var upload = middleware.multerSetup;
...
router.post('/updateuser',
upload,
...,
function (req, res, next) {
res.redirect('/dashboard');
}
);
--
//middleware/common.js
var multer = require('multer');
Middleware = {
//Checks whether user is logged in
isAuthenticated: function(req,res,next){
if(req.isAuthenticated()){
return next();
}
req.flash('auth',"You do not have the proper permissions to access this page");
res.redirect('/');
},
multerSetup: function(req,res,next){
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/uploads/')
},
//detects file type, then adds extension to match file type
filename: function (req, file, cb) {
var ext = "";
switch(file.mimetype){
case 'image/png':
ext = '.png';
break;
case 'image/jpeg':
ext = '.jpeg';
break;
default:
ext = '';
}
cb(null, Date.now() + ext); //Appending .jpg
}
});
var upload = multer({storage:storage, fileFilter: function (req, file, cb) {
var acceptedExt = ['.png','.jpg','.gif','.bmp'];
if (req.hasOwnProperty('file') && acceptedExt.indexOf(path.extname(file.originalname))=== -1) {
return cb(new Error('Image type not allowed: ' + path.extname(file.originalname)));
}
cb(null, true)
}});
return upload.single('upl');
}
};
module.exports = Middleware;
Error:
routes\index.js:108
upload.single('upl'),
^
TypeError: upload.single is not a function
at Object.<anonymous> (C:\Users\Tyler\WebstormProjects\volunteer\volunteerApp\routes\index.js:108:12)
You're setting up your multer middleware wrong. Your Middleware.multerSetup is a middleware function, which is then calling upload.single to set up multer (which is then never called and the request is left hanging). Move your multer upload setup outside your custom middleware and have your module export just the return function from upload.single.
Example:
Middleware = {
...
multerSetup: upload.single('upl')
...
}
Got it! Just needed to define upload in routes/index.js as a function.
var upload = middleware.multerSetup();
Brother in the separate file, declare your setup function, then your middleware and export it, and pass the "file" as a parameter in upload function and return upload.single(file):
const multer = require("multer");
function multerSetup() {
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "images");
},
filename: (req, file, cb) => {
cb(null, file.originalname);
},
});
return storage;
}
Middleware = {
upload: function (file) {
const upload = multer({ storage: multerSetup() });
return upload.single(file);
},
};
module.exports = Middleware;
In your route file write your route:
const router = require("express").Router();
const Middleware = require("../helper/uploadImages");
router.post("/", Middleware.upload("file"), (req, res) => {
res.status(200).json("file has been uploaded!");
});
module.exports = router
in index.js file just you need to import your route and use it:
const uploadImageRoute = require("./routes/uploadImages");
app.use("/api/upload", uploadImageRoute);

Categories

Resources