My code is as shown below:
var express = require('express');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var multer = require('multer');
var user = require('./../model/user');
var path = require('path');
var upload = multer();
var awsUpload = require('./../config/fileUpload.js');
var Promise = require('promise');
var item = require('./../model/items.js');
var item_image = '';
var storage = multer.diskStorage({
destination: function(req, file, callback) {
callback(null, './public/images')
},
filename: function(req, file, callback) {
item_image = file.fieldname + '-' + Date.now() + path.extname(file.originalname);
callback(null, item_image)
}
});
var itemAdd = function(req, res) {
upload = multer({
limits: {
fileSize: 1000000,
files: 1
},
storage: storage,
fileFilter: function(req, file, callback) {
var ext = path.extname(file.originalname)
if (ext !== '.png' && ext !== '.jpg' && ext !== '.gif' && ext !== '.jpeg') {
return callback(res.end('Only images are allowed'), null)
}
callback(null, true);
}
}).single('item_img');
upload(req, res, function(err) {
var foodtruck_id = req.body.foodtruck_id;
var newItem = new item();
var itemList = [];
newItem.item_name = req.body.item_name;
newItem.item_tag = req.body.item_tag;
newItem.item_description = req.body.item_description;
newItem.item_category = req.body.item_category;
newItem.item_discount_price = req.body.item_discount_price;
for (var key in req.body) {
if (req.body.hasOwnProperty(key)) {
if (key == 'item_illustrations') {
newItem.item_illustrations = req.body[key];
}
}
}
newItem.item_stock = req.body.item_status;
newItem.item_price = req.body.item_price;
if ((foodtruck_id) && (foodtruck_id.trim() != '')) {
foodtruck.findById(foodtruck_id.trim(), function(err, foodtrucks) {
if (err)
res.json({
status: '500',
message: 'There is no data available'
});
newItem.save(function(err, savedItem) {
if (!err) {
foodtrucks.item_list.push(savedItem._id);
foodtrucks.save(function(err, truck) {
foodtruck.find({
_id: truck._id
}).populate('item_list').exec(function(err, foodtrucks) {
res.json({
status: '200',
message: 'New item added successfully',
data: foodtrucks
});
});
});
} else {
res.json({
status: '500',
message: 'Error while saving new item'
});
}
});
});
}
});
};
app.js
app.post('/test',itemAddition);
Now what happens here is, when I use req.body.hasOwnProperty with x-www-formurlencoded, it works fine, but whenever I am adding it with multer (multipart-data), it gives me req.body.hasOwnProperty is not a function. Is there any way with which this thing can be solved?
req.body is a prototype-less object: it was created with Object.create(null) and so doesn’t inherit hasOwnProperty from Object.prototype. This is a good thing, because if a user passed a field named hasOwnProperty, they would be able to break your code.
Use the in operator instead, generally:
if (key in req.body) {
But in the case of the loop, you just don’t need a check at all:
for (var key in req.body) {
if (key == 'item_illustrations') {
newItem.item_illustrations = req.body[key];
}
}
And in this particular case, just get the value you want without a loop at all:
newItem.item_illustrations = req.body.item_illustrations;
Related
newbie question is following:
I'm having a hard time to get the EventEmitter to work. I already considered the documentation and several best-practive tutorials like this one: https://code.tutsplus.com/tutorials/managing-the-asynchronous-nature-of-nodejs--net-36183
The problem is, that within the user.js (Class) this.emit() hits nothing. userRoutes.js doesn't trigger any user.on(), and I really don't know why..
Any suggestions are appreciated. Hints for better structuring also. My goals are centralized endpoints (well-readable) and reusing the code for every module (like user or products/orders) within different modules, e.g. calling to update an order out of the user-endpoint.
So, let's assume, you have your node.js-Server with a module configuration, router and several classes, like this:
/server.js
global.__base = __dirname + "/";
var server = require("http").createServer();
var routes = require("./v2");
var initialize = require(global.__base + "config");
var app = initialize.globalModules();
app.use('/api/v2', routes);
app.listen('8090');
/config.js
var db;
module.exports = {
globalModules: function() {
// load the global modules
var app = require("express")();
var bodyParser = require("body-parser");
//setUp app
app.use(bodyParser.json());
return app;
},
db: function() {
var mysql = require('mysql');
var db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'node_docx'
});
db.connect(function(err) {
if (err) throw err;
});
return db;
}
};
/v2/index.js (routes)
var userRoutes = require('./userRoutes');
var routes = require('express').Router();
routes.use("/user", userRoutes);
//routes.use("/product", productRoutes);
//routes.use("/order", orderRoutes);
//...
routes.use('*', function(req, res) {
res.status(404).send({code: 404, message: 'ERR', data: "Unknown target."});
});
module.exports = routes;
/v2/userRoutes.js
var User = require("./routes/user.js");
var user = new User();
var route = require('express').Router();
route.get("/", function(req, res) {
user.on('error', function(err) {
res.status(400).send({code: 900, message: 'ERR', data: err});
});
user.on('failure', function() {
res.status(404).send({code: 901, message: 'ERR', data: "User not found!"});
});
user.on('success', function(result) {
res.status(200).send({code: 200, message: 'OK', data: result});
});
user.getAll();
});
module.exports = route;
/v2/routes/user.js
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var initialize = require(global.__base + "/config");
var db = initialize.db();
function User() {
EventEmitter.call(this); //edit: added, but has not solved the problem
var self = this; //solved it!
function _checkForErrors(error, rows, reason) {
if (error) {
self.emit('error', error);
return true;
}
if (rows.length < 1) {
self.emit('failure', reason);
return true;
}
return false;
}
function _getData(error, rows) {
if (_checkForErrors(error, rows)) {
return false;
} else {
self.emit('success', rows);
}
}
function getAll() {
db.query("SELECT * FROM patient", _getData);
}
this.getAll = getAll;
}
util.inherits(User, EventEmitter);
module.exports = User;
It's quite simple, you forgot EventEmiter.call(this) in function User.
function User() {
EventEmitter.call(this);
var self = this;
function _checkForErrors(error, rows, reason) {
if (error) {
self.emit('error', error);
return true;
}
if (rows.length < 1) {
self.emit('failure', reason);
return true;
}
return false;
}
function _getData(error, rows) {
if (_checkForErrors(error, rows)) {
return false;
} else {
self.emit('success', rows);
}
}
function getAll() {
db.query("SELECT * FROM patient", _getData);
}
this.getAll = getAll;
}
In my project the admins have the ability to upload MP3 files and submit parameters to it like song name.
I decided using multer middleware for handling multipart/form-data.
My problem is req.body.gender returns always undefined, because I have to use it inside the uploadSong listener. I want to upload the song when the gender is zero.
index.ejs
<form method="post" action="/acp" role="publish" enctype="multipart/form-data">
<div class="form-group">
<input type="file" name="song" id="song" accept="audio/mpeg">
</div>
<input type="checkbox" name="gender" checked data-toggle="toggle" data-on="Male" data-off="Female">
</form>
app.js
var uploadSong = upload.single('song');
app.post('/acp', isLoggedIn, function (req, res) {
console.log(req.body.gender); // returns "undefined"
if(req.body.gender == 0) { // returns "undefined"
uploadSong(req, res, function (err) {
if (err) {
res.send('uploaded');
return;
}
res.redirect('/');
});
}
});
(A) Not possible with multer.
(B) Use busboy. It uses streams to parse the form data and so you can get form elements values before the file upload and the fields are made available as events.
(C) Another solution (if you prefer using multer) is to use multer but add a header to send the value of the parameter to check before file upload. Headers are available as soon as the request reaches the server.
by using multer form-data parser you can parse form and access req.body before multer starts just register this app middle-ware:
import * as multer from "multer";
// parse form-data
app.use(multer().any());
This is my sample code, it is woking fine, if you need further explanation please let me know. hope helpful.
var Hotel = require('../models/hotel');
var path = require('path');
var multer = require('multer');
var uplodedImages = [];
var storageHotelGallery = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads/hotelGallery');
},
filename: function (req, file, cb) {
console.log(req.body);
var newFileName = Date.now() + path.extname(file.originalname);
req.newFileName = newFileName;
cb(null, newFileName);
uplodedImages.push(newFileName);
}
});
var uploadHotelGallery = multer({ storage: storageHotelGallery}).fields([{ name: 'imgArr', maxCount: 8 }]);
module.exports = function(router) {
// ADD HOTEL BASIC DATA TO CREATE HOTEL OBJECT
router.post('/createHotelStep1', function(req, res) {
if( req.body.name == null || req.body.name == '' ) {
res.json({ success: false, message: "Hotel name required" });
res.end();
}
else if( req.body.addressline1 == null || req.body.addressline1 == '' ) {
res.json({ success: false, message: "Address line 1 is required" });
res.end();
}
else if( req.body.city == null || req.body.city == '') {
res.json({ success: false, message: "City is required" });
res.end();
}
else {
var hotel = new Hotel();
hotel.name = req.body.name;
hotel.addressline1 = req.body.addressline1;
hotel.addressline2 = req.body.addressline2;
hotel.phone = req.body.phone;
hotel.city = req.body.city;
hotel.email = req.body.email;
hotel.save(function(err) {
if (err) {
res.json({ success: false, message: "Unable to Complete Hotel Step 1" });
} else {
res.json({ success: true, message: 'Create Hotel Step 1 Complete', _id : hotel._id });
}
});
}
});
router.post('/createHotelGallery', function (req, res, next) {
uplodedImages = [];
uploadHotelGallery(req, res, function(err) {
if(err) {
res.json({ success: false, message: 'Could not upload images'});
res.end();
}
else {
Hotel.findOne({ _id:req.body._id }).populate('users').exec(function (err, hotel) {
if (err) {
res.json({ success: false, message: 'Could not save uploaded images to database'});
res.end();
}
else {
for(var x=0; x<uplodedImages.length; x++)
hotel.images.push(uplodedImages[x]);
hotel.save();
res.json({ success: true, message: 'Gallery image uploaded' });
res.end();
}
});
}
});
});
return router;
}
This is my sample code, it is woking fine
const upload = multer({
storage,
fileFilter(req, file, cb) {
if(req.body.name===''){
return cb(new Error('Invalid name'), false)
}
const extname = path.extname(file.originalname).toLowerCase() === '.gz'
const mimetype = file.mimetype === 'application/x-gzip'
if (mimetype && extname) {
return cb(null, true)
} else {
return cb(new Error('Invalid mime type'), false)
}
},
})
I am using ng-file-upload to upload images and its title using the MEAN stack. Currently I can save the image but I am unable to fetch the data sent along.
Controller:
module.exports = function ($scope, Upload) {
let vm = this;
vm.uploadImage = function () {
if (vm.file) {
vm.file.upload = Upload.upload({
url: '/uploads/gallery',
method: 'POST',
data: { title: vm.title },
file: vm.file
});
vm.file.upload.then(function (response) {
$timeout(function () {
vm.file.result = response.data;
});
}, function (response) {
if (response.status > 0) { }
vm.errorMsg = response.status + ': ' + response.data;
}, function (evt) {
vm.file.progress = Math.min(100, parseInt(100.0 *
evt.loaded / evt.total));
});
}
}
vm.browseImage = function (file, errFiles) {
vm.file = file;
vm.errFile = errFiles && errFiles[0];
}
}
Route:
router.post('/gallery', (req, res) => {
//multers disk storage settings
let folder = './public/assets/images/gallery/';
let filename = '';
let imageLocation = '';
let thumbLocation = '';
let response = '';
//console.log(req.form);------throws undefined
//console.log(req.body);------throws undefined
let storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, folder)
},
filename: function (req, file, cb) {
var datetimestamp = Date.now();
filename = file.fieldname + '-' + datetimestamp + '.' + file.originalname.split('.')[file.originalname.split('.').length - 1];
imageLocation = folder + filename;
thumbLocation = folder + 'thumb' + filename;
cb(null, filename)
}
});
//multer settings
let upload = multer({
storage: storage
}).single('file');
upload(req, res, function (err) {
if (err) {
res.json({ error_code: 1, err_desc: err });
return;
}
else {
response = { fileCreated: true };
}
})
});
module.exports = router;
How can I get the string in my form in the route?
upload.single(...) is an express request handler. Multiple request handlers can be used with a router matcher such as the 'router.post' function in your code.
Thus, instead of having only a single request handler as you previously did as follows:
router.post('/gallery', (req, res) => {
...
...
}
You can rewrite your router like this:
router.post('/gallery', upload.single('file'), (req, res) => {
...
...
}
..where you use multiple request handlers.
In order to establish that, you should define multer instance outside your initial router matcher and your file should eventually look like this:
//i assume you have these in your file
const express = require("express");
const multer = require("multer");
let router = express.Router();
//multers disk storage settings
const folder = './public/assets/images/gallery/';
const filename = '';
const imageLocation = '';
const thumbLocation = '';
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, folder)
},
filename: function (req, file, cb) {
var datetimestamp = Date.now();
this.filename = file.fieldname + '-' + datetimestamp + '.' + file.originalname.split('.')[file.originalname.split('.').length - 1];
this.imageLocation = folder + filename;
this.thumbLocation = folder + 'thumb' + filename;
cb(null, filename)
}
});
//multer settings
const upload = multer({
storage: storage
});
router.post('/gallery', upload.single(), (req, res) => {
console.log(res.json);
});
module.exports = router;
I'm wondering why req.session.username is undefined in the tag >>>DOESNT WORK<<< while it does work in the tag >>>THIS DOES WORK<<< . I brought in req as an argument to my module but it seems I'm supposed to do something else? The /ajax route is accessed via a ajax call and it does set the session variable in >>>THIS DOES WORK<<<
//index.js file
var express = require('express');
var router = express.Router();
var app = express();
var functions = require('../public/javascripts/functions.js');
router.post('/ajax', function(req, res , next){
var username = req.param("username");
var password = req.param("password");
var operation = req.param("operation");
else if (operation === "validate")
{
async.series([
function()
{
functions.validate(username, password, req);
}
], function(err,result)
{
if (err)
return console.log(err);
console.log(result);
});
//req.session.username = "yaryar"; >>>THIS DOES WORK<<<
}
var strings = ["rad", "bla", "ska"]
console.log('body: ' + JSON.stringify(req.body));
console.log("AJAX RECEIVED");
res.send(strings);
});
module.exports = router;
functions.js file:
module.exports = {
validate: function(username, password, req) {
var url = 'mongodb://localhost';
var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
var ObjectId = require('mongodb').ObjectID;
MongoClient.connect(url, function(err, db)
{
assert.equal(null, err);
console.log("Connected correctly to server.");
var cursor = db.collection('users').find({username : username});
cursor.each(function(err,doc,req)
{
assert.equal(err, null);
if (doc != null)
{
console.log("user found: " + doc.username);
req.session.username = "ttyy"; // >>>DOESNT WORK<<<
return true
}
else
{
console.log("user not found");
return false;
}
});
//db.close();
});
}
};
you're overwriting req by doing cursor.each(function(err,doc,req) change it to cursor.each(function(err,doc,arr) and it will work
I’m using this tutorial, which is based on the MEAN stack.
I want to store photographs that users can upload with MongoDB on a server.
included: Angular directive to upload files
create-spot.client.view.html
<div data-ng-controller="SpotsCreateController">
<form class="form-signin" data-ng-submit="create(picFile)" novalidate>
<label>Upload an image</label>
<input type="file" id="articleimage" ng-model="picFile" ng-file-select="" ng-file-change="generateThumb(picFile[0], $files)" multiple name="file" accept="image/*">
<img ng-show="picFile[0].dataUrl != null" ng-src="{{picFile[0].dataUrl}}" class="img-thumbnail" height="50" width="100">
<span class="progress" ng-show="picFile[0].progress >= 0">
<div style="width:{{picFile[0].progress}}%" ng-bind="picFile[0].progress + '%'" class="ng-binding"></div>
</span>
<span ng-show="picFile[0].result">Upload Successful</span>
<input type="submit" class="btn btn-lg btn-primary btn-block" ng-click="uploadPic(picFile)">
<div data-ng-show="error">
<strong data-ng-bind="error"></strong>
</div>
</form>
</div>
view-spot.client.view.html
<div data-ng-controller="SpotsViewController">
<section data-ng-init="findOne()">
<img ng-src="data:image/jpeg;base64,{{spot.image}}" id="image-id" width="200" height="200"/>
</section>
</div>
application.js
var app = angular.module('newApp', ['ngAnimate', 'ngCookies', 'ngResource', 'ngRoute', 'ngSanitize', 'ngTouch', 'ui.bootstrap', 'users', 'spots']);
spots.create.client.controller.js
angular.module('spots').controller('SpotsCreateController', ['$scope', '$timeout', 'Authentication', 'Spots', '$location'
function($scope, $timeout, Authentication, Spots, $location) {
$scope.authentication = Authentication;
$scope.fileReaderSupported = window.FileReader !== null;
$scope.create = function(picFile) {
var spot = new Spots({
title: this.title,
description: this.description,
image: null
});
spot.$save(function(response) {
$location.path('spots/' + response._id);
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
$scope.doTimeout = function(file) {
$timeout( function() {
var fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = function(e) {
$timeout(function() {
file.dataUrl = e.target.result;
});
};
});
};
$scope.generateThumb = function(file) {
if (file) {
if ($scope.fileReaderSupported && file.type.indexOf('image') > -1) {
$scope.doTimeout(file);
}
}
};
spot.server.model.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var SpotSchema = new Schema({
...
image: {
type: String,
default: '',
required: false
}
});
mongoose.model('Spot', SpotSchema);
spots.server.routes.js
var multiparty = require('connect-multiparty'),
multipartyMiddleware = multiparty();
module.exports = function(app) {
app.route('/api/spots')
.get(spots.list)
.post(users.requiresLogin, multipartyMiddleware, spots.create);
app.route('/api/spots/:spotId')
.get(spots.read)
.put(users.requiresLogin, spots.hasAuthorization, spots.update)
.delete(users.requiresLogin, spots.hasAuthorization, spots.delete);
app.param('spotId', spots.spotByID);
};
spots.server.controller.js
var mongoose = require('mongoose'),
fs = require('fs'),
Spot = mongoose.model('Spot');
exports.create = function(req, res) {
if (req.files.file) {
var file = req.files.file;
}
var spot = new Spot(req.body);
spot.creator = req.user;
fs.readFile(file.path, function (err,original_data) {
if (err) {
return res.status(400).send({
message: getErrorMessage(err)
});
}
var base64Image = original_data.toString('base64');
fs.unlink(file.path, function (err) {
if (err) {
console.log('failed to delete ' + file.path);
} else {
console.log('successfully deleted ' + file.path);
}
});
spot.image = base64Image;
spot.save(function(err) {
if (err) {
return res.status(400).send({
message: getErrorMessage(err)
});
} else {
res.json(spot);
}
});
});
};
What did I do wrong? Please consider that I want to limit the file sizes, and I thought using base64 is good start. The issue is that the photograph is not stored in the database because the controller doesn’t work with the rest.
What exactly is the problem you are experiencing, and at what step is it going wrong?
For Express backend apps I usually use multer middleware for handling file uploads. Also, I create separate routes/controllers for dealing with the files rather than trying to process them at the same time I'm saving the parent object. This allows me to separate the logic nicely and not worry about the parent object not being saved when the file upload fails. You could use the JS API for ng-file-upload to handle that in Angular.
Example routes in Express (we have a "club" with a "logo" image here):
router.post(
'/logo',
ensureAuthenticated, ensureAdmin,
logoCtrl.upload,
logoCtrl.save
);
router.get(
'/:clubId/logo.*',
logoCtrl.stream
);
Example controller methods:
let multer = require('multer');
module.exports = {
/**
* Upload logo
*/
save(req, res, next) {
//Get club and file
let club = req.user.club;
let file = req.file;
//Update
club.logo = {
data: file.buffer,
mimeType: file.mimetype
};
//Save
club.save()
.then(() => {
res.end();
})
.catch(next);
},
/**
* Stream logo
*/
stream(req, res, next) {
let club = req.club;
res.contentType(club.logo.mimeType);
res.send(club.logo.data);
},
/**
* Upload middleware
*/
upload(req, res, next) {
//Create upload middleware
let upload = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 50000000
}
}).single('logo');
//Use middleware
upload(req, res, next);
}
};
So as you can see, it's quite simple with multer and all you really need is one route for uploading the logo, with two controller methods, one to register the multer middleware and process the file, and the other to save it to the MongoDB (in this case attached to the club in the request).
Just make sure that ng-file-upload uses the same field name for uploading the file as multer is expecting. In the above example that's "logo". If you're unsure, check in the request what your client app is sending to the server and make sure the server app is expecting the same field name.
Let me know if you have further trouble.
You can use formidable and gridfs-stream
//controller
var mongoose = require('mongoose'),
fs = require('fs'),
Spot = mongoose.model('Spot');
exports.create = function(req, res) {
handleRequest(req, function(err, spot) {
if(err) {
return res.status(400).send({
message: getErrorMessage(err)
});
}
res.json(spot);
});
};
function handleRequest(req) {
var spot = new Spot(req.body);
spot.creator = req.user;
var formidable = require('formidable');
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
if (err) {
return done(err);
}
var file = files.qqfile;
if (file && file.name && file.name.trim !== '') {
if (file.size > 5000000) {
message = 'file is too large';
}
if (!file.type) {
message = 'file is not an image';
}
if (file.type.indexOf('image/') !== 0) {
message = 'file is not an image type';
}
}
if (message) {
logger.info('Uploading failed', file, message);
return done(message);
}
uploadFile(mongoose.connection, 'Pictures', files.qqfile, require('uuid').v1(), function(err) {
if (err) {
return done(err);
}
if (!data) return done(false, null);
if (typeof data === 'string') {
data = JSON.parse(data);
}
logger.info('[PHOTOS]', 'Uploaded', data.filename);
photo = {
unique_id: token,
name: file.name,
contentType: file.type
};
spot.photos = spot.photos || [];
spot.photos.push(photo);
spot.markModified('photos');
spot.save(done);
});
});
}
function uploadFile(DB, className, data, token, callback) {
var grid = require('gridfs-stream');
var gfs = grid(DB.db, mongoose.mongo);
var writestream = gfs.createWriteStream({
filename: token,
root: className
});
writestream.on('close', function (file) {
return callback(null, file);
});
if (data.path) {
var fs = require('fs');
if (!fs.existsSync(data.path)) {
return callback(false);
}
var pipe = false;
if (data.pipe) {
pipe = data.pipe;
} else {
var fs = require('fs');
if (!fs.existsSync(data.path)) {
return callback(false);
}
var rs = fs.createReadStream(data.path);
pipe = rs.pipe.bind(rs);
}
return pipe(writestream);
} else {
logger.error('[PHOTO] no path', data);
}
callback(false);
}