Image upload and retrieve using Node Express 4 MongoDB - angularjs

I have created a simple login and signup using MEAN stack languages. I have a profile page where user can edit his info like name, address, etc. I am able to store and retrieve input type "text" fields. How can i add image upload feature and retrieve uploaded images? Im new to mean stack so can anyone please help me? Thanks.
My server.js is this
require('rootpath')();
var express = require('express');
var app = express();
var session = require('express-session');
var bodyParser = require('body-parser');
var expressJwt = require('express-jwt');
var config = require('config.json');
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(session({ secret: config.secret, resave: false, saveUninitialized: true }));
// use JWT auth to secure the api
app.use('/api', expressJwt({ secret: config.secret }).unless({ path: ['/api/users/authenticate', '/api/users/register'] }));
// routes
app.use('/login', require('./controllers/login.controller'));
app.use('/register', require('./controllers/register.controller'));
app.use('/app', require('./controllers/app.controller'));
app.use('/api/users', require('./controllers/api/users.controller'));
// make '/app' default route
app.get('/', function (req, res) {
return res.redirect('/app');
});
// start server
var server = app.listen(3000, function () {
console.log('Server listening at http://' + server.address().address + ':' + server.address().port);
});
HTML to save and view saved data looks like this
<h1>My Account</h1>
<div class="form-container">
<form method="post" >
<div class="form-group">
<label for="firstName">First name</label>
<input type="text" id="firstName" class="form-control" ng-model="vm.user.firstName" required />
</div>
<div class="form-group">
<label for="lastName">Last name</label>
<input type="text" id="lastName" class="form-control" ng-model="vm.user.lastName" required />
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" class="form-control" ng-model="vm.user.username" required />
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" class="form-control" ng-model="vm.user.password" />
</div>
<div class="form-group">
<label for="address">Address</label>
<input type="text" id="password" class="form-control" ng-model="vm.user.address" />
</div>
<div class="form-group">
<button class="btn btn-primary" ng-click="vm.saveUser()">Save</button>
<button class="btn btn-danger" ng-click="vm.deleteUser()">Delete</button>
</div>
</form>
</div>
<!-- View Data -->
<div class="col-md-12">
<h1>Hi {{vm.user.firstName}}!!</h1>
<p><strong>User Name :</strong> {{vm.user.username}} </p>
<p><strong>Full Name : </strong> {{vm.user.firstName}} {{ vm.user.lastName}}</p>
<p><strong> Your address is : </strong> {{vm.user.address}}</p>
</div>
and app.js looks like this
(function () {
'use strict';
angular
.module('app')
.controller('Account.IndexController', Controller);
function Controller($window, UserService, FlashService) {
var vm = this;
vm.user = null;
vm.saveUser = saveUser;
vm.deleteUser = deleteUser;
initController();
function initController() {
// get current user
UserService.GetCurrent().then(function (user) {
vm.user = user;
});
}
function saveUser() {
UserService.Update(vm.user)
.then(function () {
FlashService.Success('User updated');
})
.catch(function (error) {
FlashService.Error(error);
});
}
function deleteUser() {
UserService.Delete(vm.user._id)
.then(function () {
// log user out
$window.location = '/login';
})
.catch(function (error) {
FlashService.Error(error);
});
}
}
})();
and user.service.js which is used to update data to db is
function updateUser() {
// fields to update
var set = {
firstName: userParam.firstName,
lastName: userParam.lastName,
username: userParam.username,
address: userParam.address,
};
// update password if it was entered
if (userParam.password) {
set.hash = bcrypt.hashSync(userParam.password, 10);
}
db.users.update(
{ _id: mongo.helper.toObjectID(_id) },
{ $set: set },
function (err, doc) {
if (err) deferred.reject(err.name + ': ' + err.message);
deferred.resolve();
});
}

To build the image upload feature, You need to add functionality to both the frontend and backend.
You need to have a custom image upload directive or component in the angularjs and Some kind of upload handler package in the Express Server as well.
Keeping all this in mind, Google will surely lead you to best package available for you.
One code sample can be found here : https://gist.github.com/keithics/bf0e13feaee5631fa936b7b203029cd4
Other: https://github.com/nervgh/angular-file-upload
There are huge number of libraries available for the same.

Related

Why am I getting a 404 error when using ngresource save?

I'm following an old Pluralsight tutorial that is no longer updated and have hit a wall: I am using Visual Studio 2013 and Angular JS v1.4, and have to use this tutorial as we are using this version of AngularJS at work.
HTML TEMLPLATE: NewEvent.html
<div style="padding-left:20px; padding-right: 20px">
<div class="container">
<h1>New Event</h1>
<hr />
<form name="newEventForm">
<fieldset>
<label for="eventName">Event Name:</label>
<input id="eventName" type="text" required ng-model="event.name" placeholder="Name of your event..." />
<label for="eventDate">Event Date:</label>
<input id="eventDate" type="text" required ng-pattern="/\d\d/\d\d/\d\d\d\d/" ng-model="event.date" placeholder="format (mm/dd/yyyy)..." />
<label for="eventTime">Event Time:</label>
<input id="eventTime" type="text" required ng-model="event.time" placeholder="Start and end time..." />
<label for="eventLocation">Event Location:</label>
<input id="eventLocation" type="text" required ng-model="event.location.address" placeholder="Address of event..." />
<br />
<input id="eventCity" type="text" required ng-model="event.location.city" class="input-small" placeholder="City..." />
<input id="eventProvince" type="text" ng-model="event.location.province" class="input-small" placeholder="Province..." />
<div>{{event.name}}</div>
<label for="eventImageUrl">Image:</label>
<input id="eventImageUrl" type="url" ng-model="event.imageUrl" class="input-xlarge" placeholder="Url of image..." />
</fieldset>
<img ng-src="{{event.imageUrl}}" src="" />
<br />
<br />
<button type="submit" ng-disabled="newEventForm.$invalid" ng-click="saveEvent(event, newEventForm)" class="btn btn-primary">Save</button>
<button type="button" ng-click="cancelEdit()" class="btn btn-default">Cancel</button>
</form>
</div>
CONTROLLER: EditEventController.js
'use strict'
eventsApp.controller('EditEventController',
function EditEventController($scope, eventData) {
$scope.event = {};
$scope.saveEvent = function (event, newEventForm) {
if (newEventForm.$valid) {
eventData.save(event)
.$promise
.then(function (response) { console.log('success', response) })
.catch(function (response) { console.log('failure', response) });
}
};
$scope.cancelEdit = function () {
window.location = "./EventDetails.html";
}
});
SERVICE: EventData.js
eventsApp.factory('eventData', function ($resource) {
var resource = $resource('data/event/:id.json', { id: '#id' }, { "getAll": { method: "GET", isArray: true, params: { something: "foo" } } });
return {
getEvent: function () {
return resource.get({ id: 1 });
},
save: function (event) {
event.id = 999;
return resource.save(event);
}
}
});
I had to specify '.json' in the var resource line and that's the only change I made to the code given by the tutors, because another page (which uses the getEvent function in the service) wouldn't work unless I did that.
When I click save, I get the 'failure' response in the console even though the model for the event has built correctly.
Screenshot of error in console:-
File structure:-
The 404 error appears to be attached to the save destination, even though as far as I can tell the URL is correct. How do I correctly use ngresource.save to save this file?
web-server.js - check get & post path
const express = require('express');
const path = require('path');
const events = require('./eventsController');
const app = express();
const rootPath = path.normalize(__dirname + '/../');
const bodyParser = require('body-parser');
const port = 8000;
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static(rootPath + '/app'));
app.get('/data/event/:id', events.get);
app.post('/data/event/:id', events.save);
app.listen(port, function () {
console.log('Listening on port ' + port + '...');
});
EventData.js - check at the resource path
'use strict';
eventsApp.factory('eventData', function ($resource) {
var resource = $resource('/data/event/:id', { id: '#id' });
return {
getEvent: function () {
return resource.get({ id: 2 });
},
save: function (event) {
event.id = 999;
return resource.save(event);
}
};
});

AngularJS dindn't work in firefox

I wrote a login for a web-application. In chrome the login works but not in firefox.
<form ng-submit="$ctrl.login()">
<div class="cClearFloat">
<input type="text" name="Benutzername" placeholder="Benutzername" ng-model="$ctrl.userName">
</div>
<div class="cClearFloat">
<input type="password" name="Passwort" placeholder="Passwort" ng-model="$ctrl.password">
</div>
<div class="cClearFloat">
<div class="cButtonLogin" class="button">
<button class="cLinkLogin" type="submit">Anmelden</button>
</div>
</div>
</form>
login call:
login() {
this.userService.login(this.userName, this.password);
}
login:
login(userName, password) {
this.$http.post("http://localhost:20670/login", { UserName: userName, Password: password }).then((response) => {
this.userInfo = this.hateoasService.fromData(response.data);
this.$http.defaults.headers.common.LoginToken = this.userInfo.LoginToken;
this.isLoggedIn = true;
}, (response) => {
swal({
title: response.data.error,
type: 'error'
})
});
}
All what happens when I test it in firefox is that it reloads the page. Does someone know why it's not working? Is it possible that it makes the reload is because angularjs isn't available and he takes the default from ng-submit?
in my console I had this error
Use ng-submit on your form and change the button type to submit
On your view:
<div ng-app="myApp" ng-controller="myCtrl">
<form ng-submit="login()">
<input type="text" name="Benutzername" placeholder="Benutzername" ng-model="model.userName">
<input type="password" name="Passwort" placeholder="Passwort" ng-model="model.password">
<button class="cLinkLogin" type="submit">Anmelden</button>
</form>
</div>
On controller :
var app = angular.module("myApp", []);
app.controller("myCtrl", myCtrl);
function myCtrl($scope) {
$scope.model = {};
$scope.login = function() {
alert("username: " + $scope.model.userName + ", password: " + $scope.model.password);
}
}
See http://codepen.io/anon/pen/dNgvJp?editors=1010

Did Firebase authentication change over night?

I am using Firebase email authentication. It worked pretty well yesterday, but for some reason, it suddenly stopped working this morning, and I have not touched the authentication part in the code at all. I am wondering if there is any changes in Firebase that i am not aware? or I did something may affect it?
Here is the code in controller:
// Home controller
.controller('LoginCtrl', ['$scope', '$firebaseAuth', function($scope, $firebaseAuth) {
// Auth Logic will be here
var firebaseObj = new Firebase("https://xxxx/");
var loginObj = $firebaseAuth(firebaseObj);
$scope.user = {};
$scope.SignIn = function(e){
e.preventDefault(); //To prevent from refresh
var email = $scope.user.username + '#whateverdomain.com';
var password = $scope.user.password;
console.log('Authentication start inside SignIn:' + );
loginObj.$authWithPassword({
email: email,
password: password
})
.then(function(user) {
//Success callback
console.log('Authentication successful');
$location.path('/dashboard');
}, function(error) {
//Failure callback
console.log(error.code);
if(error.code === "INVALID_USER") {
console.log('INVALID_USER');
$scope.loginForm.username.$invalid = true;
}
if(error.code === "INVALID_PASSWORD") {
console.log('INVALID_Password');
$scope.loginForm.password.$invalid = true;
}
});
}
}]);
Here is the view:
<body ng-controller="LoginCtrl">
<div class="container-login">
<div style="padding-bottom:0px; ">
<h1> Login</h1>
</div>
<form class="form-signin" name="loginForm" style="color:#C3BCB6;">
<div class="form-group" ng-class="{'has-error':loginForm.username.$invalid}">
<label>Username</label>
<input ng-model="user.username" type="text" name="username" class="form-control" placeholder="Input your username" ng-minlength="5" ng-maxlength="12"></input>
</div>
<div class="form-group" ng-class="{ 'has-error' : loginForm.password.$invalid }">
<label>Password</label>
<input ng-model="user.password" type="password" name="password" class="form-control" placeholder="Password" ng-minlength="8"></input>
</div>
<button type="button" ng-click="SignIn($event)" ng-disabled="!user.username || !user.password" class="btn btn-lg btn-primary btn-block">Sign in</button>
</form>
</div>
When authentication success, the user is supposed to redirect to the dashboard, or when it failed, it will show error message in the console.
Currently, it only shows "Authentication starts", but no "Authentication success" or any error message, from error.code.
I even checked out previous git commit, the authentication still did not work. Any thoughts? thanks!

Unable to pass Stripe token to backend using Angular and Node

I am trying to process a Stripe charge in Angular using the Angular Payments module. I am able to get the token from Stripe, but receive the following error on the client side when I attempt to submit the token to my Express server:
ReferenceError: token is not defined
Any thoughts on how to resolve this?
Here's the relevant code:
Controller:
myApp.controller('PaymentFormCtrl',
function($scope){
$scope.handleStripe = function(status, response){
console.log('response', status, response);
if(response.error) {
console.log('error');// there was an error. Fix it.
} else {
console.log('no error');
token = response.id;
return $http.post('http://localhost:8080/api/payments', payment);
}
};
});
HTML for form:
<form stripe-form="handleStripe" name="myForm">
<div class="span3">
<label for="">Card number</label>
<input type="text" class="input-block-level" ng-model="number" payments-validate="card" payments-format="card" payments-type-model="type" ng-class="myForm.number.$card.type"/>
</div>
<div class="span1">
<label for="">Expiry</label>
<input type="text" class="input-block-level" ng-model="expiry" payments-validate="expiry" payments-format="expiry" />
</div>
<div class="span3">
<label for="">Name on card </label>
<input type="text" class="input-block-level">
</div>
<div class="span1">
<label for="">CVC</label>
<input type="text" class="input-block-level" ng-model="cvc" payments-validate="cvc" payments-format="cvc" payments-type-model="type"/>
</div>
<div class="span4">
<button type="submit" class="btn btn-primary btn-large">Submit</button>
</div>
</form>
Server Javascript:
apiRouter.route('/payments')
.post(function(req, res) {
var stripeToken = request.body.stripeToken;
var charge = stripe.charges.create({
amount: 1000,
currency: "usd",
source: stripeToken,
description: "payinguser#example.com"
}, function (err, charge) {
if (err && err.type === 'StripeCardError') {
}
});
});
One thing to fix for sure is that in the server side your request is 'req' yet you are trying to get the token from
request.body.stripeToken,
should be
req.body.stripeToken

MEAN stack ng-upload-file

I am currently using MEAN.js to create an app and I scaffolded a simple entity called Campaign. I would like each Campaign to have a picture associated. Therefore, I would like to change the CRUD interface to be able to upload a file to the back end.
I injected the ng-file-upload plugin to create the FE with Angular. On the Node.js side, I installed the multer plugin to help me save the file into a folder (e.g. ./uploads). The thing is that I do not quite get the flow and I was hoping for a suggestion.
Please, find below the view:
<section data-ng-controller="CampaignsController">
<div class="page-header">
<h1>New Campaign</h1>
</div>
<div class="col-md-12">
<form class="form-horizontal" data-ng-submit="create()" novalidate>
<fieldset>
<div class="form-group">
<label class="control-label" for="name">Name</label>
<div class="controls">
<input type="text" data-ng-model="name" id="name" class="form-control" placeholder="Name" required>
</div>
</div>
<div class="form-group">
<button ng-file-select ng-model="token">Upload the token</button>
<div ng-file-drop ng-model="token" class="drop-box"
drag-over-class="{accept:'dragover', reject:'dragover-err', delay:100}"
accept="image/*">
Drop image file here
</div>
<div ng-no-file-drop>Image drop is not supported for this browser.</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-default">
</div>
<div data-ng-show="error" class="text-danger">
<strong data-ng-bind="error"></strong>
</div>
</fieldset>
</form>
</div>
</section>
Then, the Angular controller action:
// Create new Campaign
$scope.create = function() {
// Create new Campaign object
var campaign = new Campaigns ({
name: this.name
});
$scope.$watch('token', function() {
$scope.upload = $upload.upload({
url: '/campaigns', //upload.php script, node.js route, or servlet url
method: 'POST', //Post or Put
headers: {'Content-Type': 'multipart/form-data'},
//withCredentials: true,
data: campaign, //from data to send along with the file
file: $scope.token, // or list of files ($files) for html5 only
//fileName: 'photo' // to modify the name of the file(s)
}).success(function (response, status) {
// Redirect after save
campaign.$save(function(response) {
$location.path('campaigns/' + response._id);
// Clear form fields
$scope.name = '';
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
}
).error(function (errorResponse) {
$scope.error = errorResponse.data;
//$scope.error = errorResponse.data.message;
});
});
};
Finally, the Node.js controller portion:
var mongoose = require('mongoose'),
errorHandler = require('./errors'),
multer = require('multer'),
Campaign = mongoose.model('Campaign'),
_ = require('lodash');
/**
* Create a Campaign
*/
exports.create = function(req, res) {
var campaign = new Campaign(req.body);
campaign.user = req.user;
multer({
dest: './uploads/'
});
campaign.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(campaign);
}
});
};
Right now, what happens is that - when I try to upload a file - the uploader does not wait for the file to be selected, but it sends the POST request immediately (why?). Moreover, I get a 400 response.
Any suggestion would be really appreciated!
Thanks
Cheers
I partially solved the problem.
This is the new view:
<section data-ng-controller="CampaignsController">
<div class="container">
<div class="page-header">
<h1>New Campaign</h1>
</div>
<div class="col-sm-12 col-md-4 col-md-offset-4">
<form class="form-horizontal" data-ng-submit="create(token)" novalidate>
<fieldset>
<div class="form-group">
<label class="control-label" for="name">Name</label>
<div class="controls">
<input type="text" data-ng-model="name" id="name" class="form-control" placeholder="Name" required>
</div>
</div>
<div class="form-group">
<label class="control-label" for="token">Token</label>
<div class="controls">
<input type="file" id="token" ng-file-select ng-model="token"/>
<p class="help-block">The token file must be a squared .png or .jpg image.</p>
</div>
</div>
<div class="form-group">
<div class="controls">
<input type="submit" class="btn btn-default col-xs-12">
</div>
</div>
<div class="form-group">
<div data-ng-show="error" class="control alert alert-danger alert-dismissible" role="alert">
<span data-ng-bind="error"></span>
</div>
</div>
</fieldset>
</form>
</div>
</div>
</section>
Then, the Angular controller action:
$scope.create = function(token) {
// Create new Campaign object
var campaign = new Campaigns ({
name: this.name
});
$scope.upload = $upload.upload({
url: '/campaigns',
method: 'POST',
headers: {'Content-Type': 'multipart/form-data'},
//withCredentials: true,
data: {
campaign: JSON.stringify(campaign)
},
file: token,
//fileName: 'token' // to modify the name of the file
}).success(function (response, status) {
// Redirect after save
$location.path('campaigns/' + response._id);
// Clear form fields
$scope.name = '';
$scope.token = '';
}
).error(function (errorResponse) {
$scope.error = errorResponse.data;
}
);
};
I am now using node multiparty for the Node.js controller:
exports.create = function(req, res) {
var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
//res.writeHead(200, {'content-type': 'text/plain'});
//res.end(util.inspect({fields: fields, files: files}));
var file = files.file[0];
var contentType = file.headers['content-type'];
var tmpPath = file.path;
var extIndex = tmpPath.lastIndexOf('.');
var extension = (extIndex < 0) ? '' : tmpPath.substr(extIndex);
// uuid is for generating unique filenames.
//var fileName = uuid.v4() + extension;
var fileName = tmpPath;
var destPath = 'uploads/' + fileName;
// Server side file type checker.
if (contentType !== 'image/png' && contentType !== 'image/jpeg') {
fs.unlink(tmpPath);
return res.status(400).send({
message: 'Unsupported file type'
});
}
fs.rename(tmpPath, destPath, function(err) {
if (err) {
return res.status(400).send({
message: 'Image is not saved'
});
}
fs.unlink(tmpPath, function() {
if (err) {
return res.status(400).send({
message: 'Impossible to delete temp file'
});
}
});
console.log(destPath);
//return res.jsonp(destPath);
});
var campaign = new Campaign(JSON.parse(fields.campaign[0]));
campaign.user = req.user;
campaign.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(campaign);
}
});
});
};
I still get an error, but I do not think is related with the file upload. What do you think?
/home/maurizio/Workspace/bdf-v1/node_modules/mongoose/lib/utils.js:413
throw err;
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (http.js:691:11)
at ServerResponse.header (/home/maurizio/Workspace/bdf-v1/node_modules/express/lib/response.js:592:10)
at ServerResponse.send (/home/maurizio/Workspace/bdf-v1/node_modules/express/lib/response.js:144:12)
at ServerResponse.jsonp (/home/maurizio/Workspace/bdf-v1/node_modules/express/lib/response.js:301:15)
at Promise. (/home/maurizio/Workspace/bdf-v1/app/controllers/campaigns.server.controller.js:67:9)
at Promise. (/home/maurizio/Workspace/bdf-v1/node_modules/mongoose/node_modules/mpromise/lib/promise.js:177:8)
Using res.status(400).send... or res.jsonp() will send data back to the client starting with the headers. Your script has these statements falling through, but the subsequent ones cannot be executed since data has already been sent to the client.
The returns you have will end the execution of the method they're invoked in, but the script will just continue to the next method where it will encounter another express send(). In your case, fs.rename will send() the 400, but will encounter another send() when it reaches the campaign.save method where it will throw the error.
Take your calls of return res.status(400).send(), and instead set the message as a string variable, and make the res.status(400).send() call in your final conditional statement if an error is present.
Essentially, make sure a send() or jsonp() invocation can only be made once in your script.

Resources