Upload image to MongoDB with Nodejs and express - javascript

I'm trying to upload a image using multer in nodejs. I configured multer to save uploaded images in the "upload" directory and when the form is submitted, the image is sent to the directory as it should. But I'm trying to save the path to that image (req.file.path) to a mongodb database and this is not working. I'm new to nodejs and mongodb and I can't figure out what the problem is.
This is the error I get:
ObjectParameterError: Parameter "obj" to Document() must be an object, got assets\uploads\uploaded_file-1518264794720.jpg
at new ObjectParameterError (C:\xampp\htdocs\projets\NodeApp01\node_modules\mongoose\lib\error\objectParameter.js:23:11)
at model.Document (C:\xampp\htdocs\projets\NodeApp01\node_modules\mongoose\lib\document.js:55:11)
at model.Model (C:\xampp\htdocs\projets\NodeApp01\node_modules\mongoose\lib\model.js:59:12)
at new model (C:\xampp\htdocs\projets\NodeApp01\node_modules\mongoose\lib\model.js:3750:13)
Here is my index.js:
var express=require('express');
var app=express();
var fs=require('fs');
var multer=require('multer');
app.set('view engine','ejs');
app.use(express.static('assets'));
var mongo=require('mongoose');
mongo.connect('mongodb://username:pass#database...');
var schema=new mongo.Schema({
item:String
});
var model1= mongo.model('todomod',schema);
var storage = multer.diskStorage({
destination: 'assets/uploads/',
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now()+ '.jpg')
}
})
var upload = multer({ storage: storage });
app.get('/',function(request,response){
response.render('index.ejs');
});
app.get('/add_item',function(request,response){
response.render('add_item.ejs');
});
app.post('/add_item',upload.single('uploaded_file'),function(req,res){
console.log(req.body);
console.log(req.file);
model1(req.file.path).save(function(err,data){
if(err) throw err
res.json(data);
})
});
app.listen(80);
console.log('Application listening on port 80....');
And here is my view (.ejs) for the form:
<body>
<% include partials/navigation.ejs %>
<h1>Adding an item to the shop</h1>
<form id="theform" action="add_item" method="POST" enctype="multipart/form-data">
Name of item:<input type="text" name="item" class="thedata"><br>
<br>
Photo of item:<input type="file" name="uploaded_file" class="thefile">
<button type="submit">Add item</button>
</form>
</body>

The issue is you are passing uploaded file path instead of file object, You can do like this to save uploaded file path in mongodb:
let db_data = {
item : req.file.path
};
model1(db_data ).save(function(err,data){
if(err) throw err
res.json(data);
})

Related

Image uploaded in my folder without extension with node js and multer

I have a weird problem with node and multer.
My script to upload an image is working, but the images are being uploaded without the extension (jpeg, png, etc).
Just like that:
4a8a1400d04b5ccdfdb829aa5ee62763
If I add an extension (jpeg) to the image, then it's displayed.
My code is like that:
const multer = require('multer');
var upload = multer({ dest: __dirname +'/uploads/' });
app.post('/test.html', upload.single('avatar'),function (req,res,next){
console.log("minetype"+req.file.mimetype);
res.render('test', {})
});
And my form:
<form action="/test.html" method="post" enctype="multipart/form-data" name="monForm">
<input type="file" name="avatar" />
<input type="submit" value="Upload">
</form>
you can configure filename with this for exemple :
which will preserve the original extension and still generate a random name using uniqid package. (feel free to replace it with something else if needed)
const uniqid = require('uniqid');
const path = require('path');
const multer = require('multer');
let upload = multer({
storage: multer.diskStorage({
destination: function (req, file, cb) {
cb(null, `${__dirname}/uploads/`);
},
filename: function (req, file, cb) {
cb(null, uniqid() + path.extname(file.originalname));
},
}),
});

How to call a multer upload function outside of handler

I would like to dynamically set the storage destination for uploading a file using multer in my express app, as well as the name of the file.
I have an express route that handles a post request as shown:
EJS:
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="somename">
<select name="p">
<option selected>ABC</option>
<option>DEF</option>
</select>
<input type="number" name="dtYear" value="2020">
<button type="submit">Upload</button>
</form>
ExpressJS:
let storageDest = './public/content/dts/' ;
let fileName;
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, storageDest)
console.log(file);
},
filename: (req, file, cb) => {
if (fs.existsSync(storageDest + file.originalname)){
fileName = file.fieldname + Date.now() + path.extname(file.originalname)
} else{
fileName = file.originalname;
}
cb(null, fileName)
}
})
const upload = multer({storage: storage})
app.post('/upload', upload.single('somename'), (req, res, next) => {
storageDestiantion += req.body.p.toLowerCase() + '/';
fileName = req.body.dtYear;
res.send('Thank you for uploading!')
})
However, I can only change the storageDestination and fileName variables before the multer upload.single() function is called for them to have any effect, as if I change them after the function call, the file is already uploaded.
Is there a way to call the upload.single() function after making the above changes, or does it have to be a parameter of the app.post() function - if it does, is there a workaround to achieve what I desire?

NodeJS - multer - change filename depending on request attributes

I know that I can change the filename with multer by means of the storage object like following:
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, process.env.UPLOAD_DIR);
},
filename: (req, file, cb) => {
cb(null, 'bla.png');
}
});
const upload = multer({ storage: storage } );
My request, besides having the file, also contains some text attributes such as name: myPic.png.
Is it possible to dynamically change the filename dependent on other request attributes or within the controller like following:
filename: (req, file, cb) => {
cb(null, `${req.body.name}.png`);
}
or
router.post('/upload', upload.single('pic'), myController.upload);
/* in controller */
upload = async (req: Request, res: Response) => {
try {
/* change the filename of multer here? */
} catch (err) {
winston.error(`Error while uploading: ${err.message}`);
winston.error(`Stack trace: ${err.stack}`);
sendJSONResponse(res, err, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Multer is the middleware which both populates req.body AND stores the file.
Also, when it reaches the filename() function, there is no guarantee that the text fields will be populated in req.body because it depends on which order the client sends them in (see last note).
From what I see, you have two options:
1) Rename the uploaded file after the multer upload middleware does its thing and populates req.body as well as req.file. So in your controller upload middleware, you'd do something like:
if (req.file) {
fs.renameSync(req.file.path, req.file.destination + req.body.name);
}
2) Change the request body text field into a query parameter. Then, inside filename() you can do a req.query.name.
Con: Not a very RESTful design, but maybe that is not so important to you.
According to the multer documentation it does not have access to req.body for other additional fields, if you test it it receives an undefined value, then a not so perfect but functional solution is the following, once the file is uploaded you can rename it as follows.
Add the native class fs for access to files option
const fs = require('fs');
In diskStorage configuration add the name you want, for example bla.png
var storage = multer.diskStorage({
destination: path.join('public/images/'),
filename: function ( req, file, cb ) {
cb(null, 'bla.png');
}
});
The form with the text field for the custom name
<form action="/upload" enctype="multipart/form-data" method="POST">
<input type="file" accept="image/*" name="photo" >
<br><!--here is the custom file name-->
<input type="text" name="file_name">
<br>
<button type="submit">Send</button>
</form>
Within the post path, once you have sent the file whose name will be bla.png, you can replace that name with the one in a field of the form by accessing req.body.field_name
router.post('/upload', upload.single('photo'), (req, res) => {
//Here change the file name bla.png for the new value in req.body.field_name + original ext of file
fs.renameSync(req.file.path, req.file.path.replace('bla.png',
req.body.field_name + path.extname(req.file.originalname)));
if(req.file) {
res.json(req.file);
}
else throw 'error';
});

req.file is undefined (multer, node.js)

I've been trying to upload an image for a while now, but req.file is still undefined. Can someone see why?
this is my page. I am able to pick an image when I click the '+' glyphicon, but on the server side req.file is still empty.
EJS file
input[type="file"] and input[type="submit"] have css styles display: none
<form action="/profile/addProfilepicture" method="post" id="form" enctype="multipart/form-data">
<span id="upload" class="glyphicon glyphicon-plus-sign"></span>
<label for="profilePic"></label>
<input id=profilePic type='file' />
<input type="submit">
</form>
<img class="profileImg"
src="<%="images/pexels-photo-370799.jpeg"%>"
alt="fail">
Client JS file
When I click the '+'glyphicon it lets me pick an image. When I do this, this will trigger the form to submit and send a post request.
$("#upload").on('click',function() {
$("input[type='file']").click();
});
$('input[type="file"]').change(function (e) {
$("input[type='submit']").click()
});
server side JS
On the server side it stops at:
TypeError: Cannot read property 'filename' of undefined
at C:\Users\Tijl Declerck\Desktop\projects\digitalNomadApp\routes\profile.js:27:38
at Immediate._onImmediate (C:\Users\Tijl Declerck\Desktop\projects\digitalNomadApp\node_modules\multer\lib\make-middleware.js:53:37)
at runCallback (timers.js:793:20)
at tryOnImmediate (timers.js:751:5)
at processImmediate [as _immediateCallback] (timers.js:722:5)
The console.logs I tried gave me this: req.body returns an empty object and req.file returns undefined.
var express = require('express');
var router = express.Router();
var multer = require('multer');
var User = require('../models/Users');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/uploads/profilePics')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
});
var upload = multer({ storage: storage }).single('myImage');
router.post('/addProfilePicture', function (req, res) {
var profilePicUrl = '';
upload(req, res, function (err) {
if (err) {
// An error occurred when uploading
} else {
console.log(req.file);
profilePicUrl = req.file.filename;
User.update({username: req.user.username}, {'profilePic.uploaded': true, 'profilePic.link': profilePicUrl}, function(err, doc){
console.log('THIS IS DONE')
});
}
});
});
You have to provide a name to your file input and it should match the single method's name, this is from multer doc:
.single(fieldname)
Accept a single file with the name fieldname. The single file will be
stored in req.file.
This is not well documented but fieldname refers to input name attribute
EJS file
<input id='profilePic' name='myImage' type='file' />
Express
...
var upload = multer({ storage: storage }).single('myImage');
...

Got issue with upload and resize image in express js

var express = require("express"),
app = express(),
formidable = require('formidable'),
util = require('util'),
fs = require('fs-extra'),
qt = require('quickthumb');
// Use quickthumb
app.use(qt.static(__dirname + '/'));
app.post('/upload', function (req, res){
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
res.writeHead(200, {'content-type': 'text/plain'});
res.write('received upload:\n\n');
res.end(util.inspect({fields: fields, files: files}));
});
form.on('end', function(fields, files) {
/* Temporary location of our uploaded file */
var temp_path = this.openedFiles[0].path;
/* The file name of the uploaded file */
var file_name = this.openedFiles[0].name;
/* Location where we want to copy the uploaded file */
var new_location = 'uploads/';
fs.copy(temp_path, new_location + file_name, function(err) {
if (err) {
console.error(err);
} else {
console.log("success!")
}
});
});
});
// Show the upload form
app.get('/', function (req, res){
res.writeHead(200, {'Content-Type': 'text/html' });
var form = '<form action="/upload" enctype="multipart/form-data" method="post">Add a title: <input name="title" type="text" /><br><br><input multiple="multiple" name="upload" type="file" /><br><br><input type="submit" value="Upload" /></form>';
res.end(form);
});
app.listen(8080);
I am a fresher on node js and express js. I followed this guy's blog to upload and resize image. The above is the code part. The upload function is working. However, in terms of resize function, if I want to resize uploaded image, I got this error:
events.js:85
throw er; // Unhandled 'error' event
^
Error: spawn identify ENOENT
at exports._errnoException (util.js:746:11)
at Process._handle.onexit (child_process.js:1053:32)
at child_process.js:1144:20
at process._tickCallback (node.js:355:11)
Did you install imagemagick? Quickthumb requires it for work.

Categories