Getting 404 trying http PUT using Node Express Postgresql - angularjs

Edit: NEVERMIND - I made a small mistake in the Angular service. My bad.
I'm trying to teach myself more backend by building a simple CMS using angular, Node and Express, and PostgreSql. I figured out how to do achieve all the CRUD functionality except UPDATE. I thought I understood what to do but I can't figure out where I'm going wrong. I keep getting a 404. What am I misunderstanding or going about the wrong way? I know this is simple but I'm pretty new so any help in understanding where I'm getting confused is really appreciated. Here's the relevant code:
HTML
<form ng-submit="updateBlogEntry(specificBlog.id, specificBlog.title, specificBlog.author, specificBlog.imageurl, specificBlog.content)">
<h2>Title:</h2>
<input type="text" ng-model="specificBlog.title"></input>
<br>
<h3>Author:</h3>
<input type="text" ng-model="specificBlog.author"></input>
<br>
<h3>Photo:</h3>
<input type="text" ng-model="specificBlog.imageurl"></input>
<br>
<h3>Content:</h3>
<textarea type="text" rows="5" cols="50" ng-model="specificBlog.content">
</textarea>
<br>
<button type="submit">Save Changes</button>
</form>
Angular Controller
var id = $stateParams.id;
var title = $stateParams.title;
var author = $stateParams.author;
var imageurl = $stateParams.imageurl;
var content = $stateParams.content;
$scope.updateBlogEntry = function(id, title, author, imageurl, content) {
adminService.updateBlogEntry(id, title, author, imageurl, content);
}
Angular Service
this.updateBlogEntry = function(id, title, author, imageurl, content) {
return $http({
method: 'PUT',
url: 'updateBlogEntry/' + id,
data: {
id: id,
title: title,
author: author,
imageurl: imageurl,
content: content
}
})
.success(function(data) {
alert("Entry Updated");
})
.error(function(data) {
alert("Error Updating");
})
Server Index
// EXTERNAL MODULES //
var express = require('express');
var bodyParser = require('body-parser');
var session = require('express-session');
var massive = require('massive');
// CONFIG //
var config = require('./config');
// EXPRESS //
var app = module.exports = express();
app.use(express.static(__dirname + './../dist'));
app.use(bodyParser.json());
// MASSIVE //
var massiveUri = config.MASSIVE_URI;
var massiveServer = massive.connectSync({
connectionString: massiveUri
});
app.set('db', massiveServer);
var db = app.get('db');
var dbSetup = require('./services/dbSetup');
dbSetup.run();
// CONTROLLERS //
var userCtrl = require('./controllers/userCtrl');
var blogCtrl = require('./controllers/blogCtrl');
// Blog Endpoints //
app.post('/api/createBlogEntry', blogCtrl.createBlogEntry);
app.get('/api/getBlogEntries', blogCtrl.readBlogEntries);
app.get('/api/getBlogEntry/:id', blogCtrl.readBlogEntry);
// BUG Why isn't this working?
app.put('/api/updateBlogEntry/:id', blogCtrl.updateBlogEntry);
// CONNECTIONS //
var port = config.PORT;
app.listen(port, function() {
console.log('Listening on port ' + port);
});
Node Controller
updateBlogEntry: function(req, res, next){
db.blogs.blog_update([
req.params.id,
req.body.title,
req.body.author,
req.body.imageurl,
req.body.content
],
function(err, results){
if (err){
console.error(err);
res.send(err);
} else {
res.send(results[0]);
}
})
}
blog_update.sql
UPDATE blogs
set
title = COALESCE($2, title),
author = COALESCE($3, author),
imageurl = COALESCE($4, imageurl),
content = COALESCE($5, content)
WHERE id = $1
RETURNING * ;
The error in the console:
angular.js:11881 PUT http://localhost:3000/updateBlogEntry/1 404 (Not Found)

You have written your URL wrong.
It should be /api/updateBlogEntry as per your Node's express routes.
change this part
` url: 'updateBlogEntry/' + id,`
It should be
url: '/api/updateBlogEntry/' + id,

Related

Can't receive data from form

The problem I currently have is that when I send my form data in React using axios or even without it I am unable to retrieve my data on the backend side using express. I am viewing the request received on the back end console but am unable to access it via code. React code is via Node on port 3000, Express on 3001. Application was created using create-react-app, here is a picture:Problem
Project Structure:
cssd -> Where I run npm start launching back end
cssd/routes/ -> Where users.js is located with the routes to obtain code
cssd/client/ -> Where i run npm start launching front end
cssd/client/src/setupProxy.js -> where my proxy route is
I've already tried creating a setupProxy.js file as well as numerous get / post methods trying to receive the data and any of the info available on stack has been searched as well but if I've missed something please let me know. Even with a console.log at the base, i still can't receive it.
Within setupProxy:
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use(proxy('/routes/*',
{ target: "http://localhost:3001/" }
));
}
Within client's App.js / React:
render() {
return (
<form id="contact-form" onSubmit={this.handleSubmit.bind(this)} method="POST">
<div className="form-group">
<label htmlFor="name">Name</label>
<input type="text" className="form-control" id="name"/>
</div>
<div className="form-group">
<label htmlFor="exampleInputEmail1">Email address</label>
<input type="text" className="form-control" id="email" aria-describedby="emailHelp"/>
</div>
<div className="form-group">
<label htmlFor="message">Message</label>
<textarea className="form-control" rows="5" id="message"></textarea>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
)
}
handleSubmit(e)
{
e.preventDefault();
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
const message = document.getElementById('message').value;
var inputData = {name, email, message};
fetch('/routes/users',
{
method: 'POST',
body: inputData
})
alert("sent data");
this.resetForm();
}
Within routes/users.js / Express code:
var express = require('express');
var router = express.Router();
const bodyParser = require("body-parser");
const app = express();
app.use(
bodyParser.urlencoded({ // basically any extra characters
extended: true
}),
bodyParser.json()
);
// Re-directs, ex entered in nothing, go to get
router.get("/routes/users", function(req, res) {
console.log("HERE 1 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data: ", name, email, message);
});
router.post("/routes/users", function(req, res) {
console.log("HERE 2 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data", name, email, message);
});
app.get("/routes/users", function(req, res) {
console.log("HERE 3 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data: ", name, email, message);
});
app.post("/routes/users", function(req, res) {
console.log("HERE 4 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data", name, email, message);
});
router.get("routes/users", function(req, res) {
console.log("HERE 5 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data: ", name, email, message);
});
router.post("routes/users", function(req, res) {
console.log("HERE 6 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data", name, email, message);
});
app.get("routes/users", function(req, res) {
console.log("HERE 7 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data: ", name, email, message);
});
app.post("routes/users", function(req, res) {
console.log("HERE 8 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data", name, email, message);
});
const port = 3000;
//console.log(`Back end is listening on port ${port}`);
app.listen(port, () => console.log(`Back end is listening on port ${port}`));
module.exports = router;
The expected results should be that I receive a console.log from one of the functions and can then work with my code from there but I'm just unable to get any of them to be called. Only thing shown in the console is on the back-end side receiving the request
Remove all proxying from the server as this is only if you are serving the client and server from the same host and port. So make sure your app.listen is on a different port than your client like so.
const port = process.env.PORT || 5000
app.listen(port, () => console.log(`listnening on ${port}`));
Is this app set up with Create React App? If so you can add a proxy to the package.json of the client (React)
"proxy": "http://localhost:5000"
Create react app uses webpack dev server to serve your app to the browser. You can use a proxy from your client to achieve want you want.
In your package.json of your react app, add "proxy": "http://localhost:3001"
Here is more info about this from the create react app docs.
Your backend code does not need any changes to make this work, in fact I would even remove all proxy setup code in your backend. Only thing needed is to add this one setting in your react app package.json.

req.body is undefined in express 4.13.1

i have a problem and i don't know the problem. Please, help me to find the solution.
In the Angular view i have:
<form ng-submit="submit()">
<input ng-model="stickie_text" type="text" id="sticky_content" />
<button type="submit" id="add_sticky" value="add a new stickie!">new sticky</button>
In the client side (AngularJS) i have this controller:
dcuApp.controller('rankingController', ['$scope', '$http',function($scope, $http) {$scope.submit = function(){
// console.log("holaaaa");
console.log($scope.stickie_text);
$http.get('/api/get-ranking',{"valor":$scope.stickie_text}).then(function(response) {
$scope.data = response.data;
console.log("RANKING: "+$scope.data);
},
function(response) {
console.debug('Error:' + response);
});
};
}]);
In the server side (Express 4.13.1) i have:
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var db = require('../model/db');
var model = require('../model/model');
var fs = require('fs');
var async = require('async');
var bodyParser = require('body-parser');
var methodOverride = require('method-override'); //used to manipulate POST
router.use(bodyParser.urlencoded({ extended: true }))
router.use(methodOverride(function(req, res){
if (req.body && typeof req.body === 'object' && '_method' in req.body) {
// look in urlencoded POST bodies and delete it
var method = req.body._method
delete req.body._method
return method
}
}));
router.get('/api/get-ranking',function(req,res,next){
console.log(req.body.valor);
});
The problem is that req.body.valor is UNDEFINED.
I know there are other postings that relate to this problem, but have not managed to fix it.
Please, I need help to get on the server side, a different req.body.valor to undefined
thanks!!! :)
You are trying to do a GET request and send data along with it. You need to use POST request to achieve what you want. You cannot send data in a GET request.
You need to change your current GET method to POST.
Replace :
router.get('/api/get-ranking',function(req,res,next){
console.log(req.body.valor);
});
with
router.post('/api/get-ranking',function(req,res,next){
console.log(req.body.valor);
});
also,
Replace :
$http.get('/api/get-ranking',{"valor":$scope.stickie_text})
with:
$http.post('/api/get-ranking',{"valor":$scope.stickie_text})
Hope this helps.

Angularjs Post request to server

How would i gather that info im sending from the client? in this case, the id?
How can I get the id?
I do use client sided request:
return $http.post('/api/kill', {id:4}, {
headers: {}
})
and when i check server sided for req.body console.log(Req.body) i do get:
{ '{"id":4}': '' }
req.body.id returns:
undefined
How can i get the id of 4?
EDIT1:
the main code is located at https://github.com/meanjs/mean
server sided code:
app.post('/api/kill', function (req, res) {
console.log(req.body); // { '{"id":4}': '' }
console.log(req.body.id); // undefined
});
You need to assign that id property to an object like
item = { id : 4 }
Lets suppose you have a text-box and the user wants to save a new item by inserting its name in it and click on submit.
Lets also suppose you are using a MongoDB collection of items, which have only id field for simplicity.
Here's what you should do to get it going easy.
Make sure you are importing bodyParser
var bodyParser = require('body-parser');
HTML - saving a new item with custom id
<div class="form-group">
<label for="id">ID</label>
<input type="text" class="form-control" id="id" ng-model="ItemController.formData.id">
</div>
<button type="submit" ng-click="ItemController.createItem()" >Submit</button>
Angular part - ItemController.js
'use strict';
angular
.module('myApp')
.controller('ItemController', ItemController);
function ItemController($http) {
var vm = this;
/** Creates a New Marker on submit **/
vm.createItem = function() {
// Grabs all of the text box fields
var itemData = {
id : vm.formData.id
};
// Saves item data to the db
$http.post('/api/kill', itemData)
.success(function(response) {
if(response.err){
console.log('Error: ' + response.err);
} else {
console.log('Saved '+response);
}
});
};
}
Route Handling - routes.js
var ItemFactory = require('./factories/item.factory.js');
// Opens App Routes
module.exports = function(app) {
/** Posting a new Item **/
app.post('/api/kill', function(req, res) {
ItemFactory.postItem(req).then( function (item) {
return res.json(item);
});
});
};
Post into MongoDB - item.factory.js
var Item = require('../models/item-model');
exports.postItem = postItem;
function postItem(item) {
return new Promise( function (resolve, reject) {
var newItem = new Item(item.body);
newItem.save(function(err) {
if (err){
return reject({err : 'Error while saving item'});
}
// If no errors are found, it responds with a JSON of the new item
return resolve(item.body);
});
});
}
If you try console.log() on the different pieces of code where I passed the item, you can properly see an object with id property.
I hope I've been helpful.
you miss the single quote :
var obj = { 'id':4 };
console.log(obj.id); //display 4
in your example :
return $http.post('/api/kill', {'id':4}, {
headers: {}
})
response you are getting is not in object form
{ '{"id":4}': '' }
it is a key value pair and key is a string
'{"id":4}'
In order to get the right value at your end your json response shoulb be like
{ { 'id':4 } }
and then it will work like
console.log(req.body); // { {"id":4} }
console.log(req.body.id); // 4
Make sure that you enabled JSON body parser in your node.js application.
var bodyParser = require('body-parser');
....
app.use(bodyParser.json());

MongooseJS Update API

I am having a 404 issue with my NodeJS API. I don't know if I am quite doing it right, I tried referring to documentation, and I feel like it's close.
MongoDB Schema
var User = mongoose.Schema({
local: {
email: String,
password: String,
handle: String,
pic: {data: Buffer, contentType: String}
}
});
NodeJS UPDATE API
app.post('/api/users', function(req, res, user) {
User.update({email : user.email,
password : user.password,
handle : user.handle,
pic : user.pic},
{$set: {
email : req.body.email,
password : req.body.email,
handle : req.body.handle,
pic : req.body.pic,
done : false
}
}, function(err, users) {
if(err) {
res.send(err);
}
res.redirect('/profile');
});
});
Controller POST API call
$scope.editProfile = function() {
$http.post('/api/users', $scope.editFormData)
.success(function(data) {
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
Any suggestions?
You are not doing a post call correct. You can't pass your post object in the URL. Your node post should look like this.
app.post('/api', upload.array(), function(req, res) {
var body = req.body; //body will be your post object
});
For a post to work you need to make sure you have the proper things added to your Node Project. The above example is using ExpressJS with require('body-parser') and require('multer'). The example you are showing will never show as a true path. For reference here is how you would do a get in node.
app.get('/getcall/*', function(){
// the * denotes any singleton parameter you wanted to pass in.
})
Here are the references I use in all my node projects. These are the basics.
var express = require('express'),
bodyParser = require('body-parser'),
multer = require('multer'),
helmet = require('helmet'),
upload = multer(),
path = require('path'),
request = require('request'),
app = express(),
http = require('http');
Also as for your angular call an $http.post looks like this and you should be using .then instead of .success.
$http.post('/api', $scope.editFormData)
.then(function successCallback(resp) {
console.log(resp.data)
}, function errorCallback(resp) {
console.log(resp)
});

Uploading images with Mongoose, Express and AngularJS

I know this has been asked many times before, and I have read almost all I could find about the subject, namely:
https://stackoverflow.com/a/25022437/1031184
Uploading images using Node.js, Express, and Mongoose
Those are the best I have found so far. My problem is tho that they still aren't very clear, there is very little documentation online at all about this and the discussion seems aimed at people who are much more advanced than I am.
So with that I would really love it if someone could please walk me though how to upload images using Mongoose, Express & AngularJS. I am actually using the MEAN fullstack. (this generator to be precise – https://github.com/DaftMonk/generator-angular-fullstack)
AddController:
'use strict';
angular.module('lumicaApp')
.controller('ProjectAddCtrl', ['$scope', '$location', '$log', 'projectsModel', 'users', 'types', function ($scope, $location, $log, projectsModel, users, types) {
$scope.dismiss = function () {
$scope.$dismiss();
};
$scope.users = users;
$scope.types = types;
$scope.project = {
name: null,
type: null,
images: {
thumbnail: null // I want to add the uploaded images _id here to reference with mongoose populate.
},
users: null
};
$scope.save = function () {
$log.info($scope.project);
projectsModel.post($scope.project).then(function (project) {
$scope.$dismiss();
});
}
}]);
I want to add the Images ID reference to project.images.thumbnail but I want to store all the information inside an Image Object using the following Schema:
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ImageSchema = new Schema({
fileName: String,
url: String,
contentType: String,
size: String,
dimensions: String
});
module.exports = mongoose.model('Image', ImageSchema);
I have also added the following https://github.com/nervgh/angular-file-upload to my bower packages.
As I say I just can't figure out how to tie it all together. And I'm not even sure if what I am trying to do is the correct way either.
--------------------------------------------------------------------------\
UPDATE:
Here is what I now have, I have added some comments detailing how I would like it to work, unfortunately I still haven't managed to get this working, I can't even get the image to start uploading, never mind uploading to S3. Sorry to be a pain but I am just finding this particularly confusing, which surprises me.
client/app/people/add/add.controller.js
'use strict';
angular.module('lumicaApp')
.controller('AddPersonCtrl', ['$scope', '$http', '$location', '$window', '$log', 'Auth', 'FileUploader', 'projects', 'usersModel', function ($scope, $http, $location, $window, $log, Auth, FileUploader, projects, usersModel) {
$scope.dismiss = function () {
$scope.$dismiss();
};
$scope.newResource = {};
// Upload Profile Image
$scope.onUploadSelect = function($files) {
$scope.newResource.newUploadName = $files[0].name;
$http
.post('/api/uploads', {
uploadName: newResource.newUploadName,
upload: newResource.newUpload
})
.success(function(data) {
newResource.upload = data; // To be saved later
});
};
$log.info($scope.newResource);
//Get Projects List
$scope.projects = projects;
//Register New User
$scope.user = {};
$scope.errors = {};
$scope.register = function(form) {
$scope.submitted = true;
if(form.$valid) {
Auth.createUser({
firstName: $scope.user.firstName,
lastName: $scope.user.lastName,
username: $scope.user.username,
profileImage: $scope.user.profileImage, // I want to add the _id reference for the image here to I can populate it with 'ImageSchema' using mongoose to get the image details(Name, URL, FileSize, ContentType, ETC)
assigned: {
teams: null,
projects: $scope.user.assigned.projects
},
email: $scope.user.email,
password: $scope.user.password
})
.then( function() {
// Account created, redirect to home
//$location.path('/');
$scope.$dismiss();
})
.catch( function(err) {
err = err.data;
$scope.errors = {};
// Update validity of form fields that match the mongoose errors
angular.forEach(err.errors, function(error, field) {
form[field].$setValidity('mongoose', false);
$scope.errors[field] = error.message;
});
});
}
};
$scope.loginOauth = function(provider) {
$window.location.href = '/auth/' + provider;
};
}]);
server/api/image/image.model.js I would like to store all image information here and use this to populate profileImage in people controller.
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ImageSchema = new Schema({
fileName: String,
url: String, // Should store the URL of image on S3.
contentType: String,
size: String,
dimensions: String
});
module.exports = mongoose.model('Image', ImageSchema);
client/app/people/add/add.jade
.modal-header
h3.modal-title Add {{ title }}
.modal-body
form(id="add-user" name='form', ng-submit='register(form)', novalidate='')
.form-group(ng-class='{ "has-success": form.firstName.$valid && submitted,\
"has-error": form.firstName.$invalid && submitted }')
label First Name
input.form-control(type='text', name='firstName', ng-model='user.firstName', required='')
p.help-block(ng-show='form.firstName.$error.required && submitted')
| First name is required
.form-group(ng-class='{ "has-success": form.lastName.$valid && submitted,\
"has-error": form.lastName.$invalid && submitted }')
label Last Name
input.form-control(type='text', name='lastName', ng-model='user.lastName', required='')
p.help-block(ng-show='form.lastName.$error.required && submitted')
| Last name is required
.form-group(ng-class='{ "has-success": form.username.$valid && submitted,\
"has-error": form.username.$invalid && submitted }')
label Username
input.form-control(type='text', name='username', ng-model='user.username', required='')
p.help-block(ng-show='form.username.$error.required && submitted')
| Last name is required
// Upload Profile Picture Here
.form-group
label Profile Image
input(type="file" ng-file-select="onUploadSelect($files)" ng-model="newResource.newUpload")
.form-group(ng-class='{ "has-success": form.email.$valid && submitted,\
"has-error": form.email.$invalid && submitted }')
label Email
input.form-control(type='email', name='email', ng-model='user.email', required='', mongoose-error='')
p.help-block(ng-show='form.email.$error.email && submitted')
| Doesn't look like a valid email.
p.help-block(ng-show='form.email.$error.required && submitted')
| What's your email address?
p.help-block(ng-show='form.email.$error.mongoose')
| {{ errors.email }}
.form-group(ng-class='{ "has-success": form.password.$valid && submitted,\
"has-error": form.password.$invalid && submitted }')
label Password
input.form-control(type='password', name='password', ng-model='user.password', ng-minlength='3', required='', mongoose-error='')
p.help-block(ng-show='(form.password.$error.minlength || form.password.$error.required) && submitted')
| Password must be at least 3 characters.
p.help-block(ng-show='form.password.$error.mongoose')
| {{ errors.password }}
.form-group
label Assign Project(s)
br
select(multiple ng-options="project._id as project.name for project in projects" ng-model="user.assigned.projects")
button.btn.btn-primary(ng-submit='register(form)') Save
pre(ng-bind="user | json")
.modal-footer
button.btn.btn-primary(type="submit" form="add-user") Save
button.btn.btn-warning(ng-click='dismiss()') Cancel
server/api/upload/index.js
'use strict';
var express = require('express');
var controller = require('./upload.controller');
var router = express.Router();
//router.get('/', controller.index);
//router.get('/:id', controller.show);
router.post('/', controller.create);
//router.put('/:id', controller.update);
//router.patch('/:id', controller.update);
//router.delete('/:id', controller.destroy);
module.exports = router;
server/api/upload/upload.controller.js
'use strict';
var _ = require('lodash');
//var Upload = require('./upload.model');
var aws = require('aws-sdk');
var config = require('../../config/environment');
var randomString = require('../../components/randomString');
// Creates a new upload in the DB.
exports.create = function(req, res) {
var s3 = new aws.S3();
var folder = randomString.generate(20); // I guess I do this because when the user downloads the file it will have the original file name.
var matches = req.body.upload.match(/data:([A-Za-z-+\/].+);base64,(.+)/);
if (matches === null || matches.length !== 3) {
return handleError(res, 'Invalid input string');
}
var uploadBody = new Buffer(matches[2], 'base64');
var params = {
Bucket: config.aws.bucketName,
Key: folder + '/' + req.body.uploadName,
Body: uploadBody,
ACL:'public-read'
};
s3.putObject(params, function(err, data) {
if (err)
console.log(err)
else {
console.log("Successfully uploaded data to my-uploads/" + folder + '/' + req.body.uploadName);
return res.json({
name: req.body.uploadName,
bucket: config.aws.bucketName,
key: folder
});
}
});
};
function handleError(res, err) {
return res.send(500, err);
}
server/config/environment/development.js
aws: {
key: 'XXXXXXXXXXXX',
secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
region: 'sydney',
bucketName: 'my-uploads'
}
All of this code is straight out of a project that depends heavily on this for large file uploads and images. Definitely checkout https://github.com/nervgh/angular-file-upload
In my view somewhere:
<div class="form-group">
<label>File Upload</label>
<input type="file" ng-file-select="onUploadSelect($files)" ng-model="newResource.newUpload">
</div>
Using the module angularFileUpload I then have in my controller:
$scope.onUploadSelect = function($files) {
$scope.newResource.newUploadName = $files[0].name;
};
https://github.com/nervgh/angular-file-upload
When the user clicks upload this gets executed where I send the file to be uploaded:
$http
.post('/api/uploads', {
uploadName: newResource.newUploadName,
upload: newResource.newUpload
})
.success(function(data) {
newResource.upload = data; // To be saved later
});
This request is sent to a controller that looks something like this:
'use strict';
var _ = require('lodash');
var aws = require('aws-sdk');
var config = require('../../config/environment');
var randomString = require('../../components/randomString');
// Creates a new upload in the DB.
exports.create = function(req, res) {
var s3 = new aws.S3();
var folder = randomString.generate(20); // I guess I do this because when the user downloads the file it will have the original file name.
var matches = req.body.upload.match(/data:([A-Za-z-+\/].+);base64,(.+)/);
if (matches === null || matches.length !== 3) {
return handleError(res, 'Invalid input string');
}
var uploadBody = new Buffer(matches[2], 'base64');
var params = {
Bucket: config.aws.bucketName,
Key: folder + '/' + req.body.uploadName,
Body: uploadBody,
ACL:'public-read'
};
s3.putObject(params, function(err, data) {
if (err)
console.log(err)
else {
console.log("Successfully uploaded data to csk3-uploads/" + folder + '/' + req.body.uploadName);
return res.json({
name: req.body.uploadName,
bucket: config.aws.bucketName,
key: folder
});
}
});
};
function handleError(res, err) {
return res.send(500, err);
}
server/components/randomString/index.js
'use strict';
module.exports.generate = function(textLength) {
textLength = textLength || 10;
var text = '';
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for(var i = 0; i < textLength; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
};
server/config/environment/development.js
server/api/upload/upload.controller.js
This is the way i used MEAN.JS for file upload.
Model
var UserSchema = new mongoose.Schema({
name:{type:String,required:true},
photo:Buffer // Image
});
Server Controller
var userPicture = function(req,res){ // Stores Picture for a user matching the ID.
user.findById(req.param('id'), function (err, user) {
console.log(req.files) // File from Client
if(req.files.file){ // If the Image exists
var fs = require('node-fs');
fs.readFile(req.files.file.path, function (dataErr, data) {
if(data) {
user.photo ='';
user.photo = data; // Assigns the image to the path.
user.save(function (saveerr, saveuser) {
if (saveerr) {
throw saveerr;
}
res.json(HttpStatus.OK, saveuser);
});
}
});
return
}
res.json(HttpStatus.BAD_REQUEST,{error:"Error in file upload"});
});
};
Client Controller
$scope.saveuserImage = function(){
$scope.upload = $upload.upload({ // Using $upload
url: '/user/'+$stateParams.id+'/userImage', // Direct Server Call.
method:'put',
data:'', // Where the image is going to be set.
file: $scope.file
}).progress(function (evt) {})
.success(function () {
var logo = new FileReader(); // FileReader.
$scope.onAttachmentSelect = function(file){
logo.onload = function (e) {
$scope.image = e.target.result; // Assigns the image on the $scope variable.
$scope.logoName = file[0].name; // Assigns the file name.
$scope.$apply();
};
logo.readAsDataURL(file[0]);
$scope.file = file[0];
$scope.getFileData = file[0].name
};
location.reload();
$scope.file = "";
$scope.hideUpload = 'true'
});
$scope.getFileData = '';
// location.reload()
};
Html
The ng-file-select is used to get the file from the client.
This works fine for me. Hope this helps.
Note: I have used HTML tag instead of jade. Suitable changes applicable while using jade.
As far as I can guess, you are binding the FileReader.onload() method inside the saveUserImage function, then the onload method will be never called as the function is never binded instead a user calls saveUserImage method before editing the image. After that, no image will be selected as the onload() method will not execute.
Try coding Client Controller it this way
//This goes outside your method and will handle the file selection.This must be executed when your `input(type=file)` is created. Then we will use ng-init to bind it.
$scope.onAttachmentSelect = function(){
var logo = new FileReader(); // FileReader.
logo.onload = function (event) {
console.log("THE IMAGE HAS LOADED");
var file = event.currentTarget.files[0]
console.log("FILENAME:"+file.name);
$scope.image = file;
$scope.logoName = file.name; // Assigns the file name.
$scope.$apply();
//Call save from here
$scope.saveuserImage();
};
logo.readAsDataURL(file[0]);
$scope.file = file[0];
$scope.getFileData = file[0].name
reader.readAsDataURL(file);
};
//The save method is called from the onload function (when you add a new file)
$scope.saveuserImage = function(){
console.log("STARGING UPLOAD");
$scope.upload = $upload.upload({ // Using $upload
url: '/user/'+$stateParams.id+'/userImage',
method:'put'
data:, $scope.image
file: $scope.file
}).progress(function (evt) {})
.success(function () {
location.reload();
$scope.file = "";
$scope.hideUpload = 'true'
});
$scope.getFileData = '';
// location.reload()
};
The HTML.
//There is the ng-init call to binding function onAttachmentSelect
<div class="form-group">
<label>File Upload</label>
<input type="file" ng-init="onAttachmentSelect" ng-model="newResource.newUpload">
</div>
Hope this clue may help you
EDIT*
Will try to explain you the different Steps you must follow to check your code:
1.- Is your input[type=file] showing? If showing, please select an image
2.- Is your input calling the onload when the image selected has changed? (a console.log should be printed with my code version)
3.- If it has been called. Make the operations you need before sending, inside the onload method (if possible)
4.- When this method has finished doing desired changes. Inform with ng-model or however you want, a variable in the object you prepared to upload, with the base64 string generated in the onload method.
When arriving this point, remember checking that:
As very big images could be sent over json with base64, it´s very important to remember changing the minimum json size in Express.js for your app to prevent rejects. This is done, for example in your server/app.js as this:
var bodyParser = require('body-parser');
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb'}));
Remember also that the method reader.readAsDataURL(file) will give you a base64 string that could act as src of the image. You don´t need more than this. This base64 is what you can save in mongoose. Then, you can use ng-model to send a variable containing the base64 in the form with the "submit" button.
Then, in the Express.js endpoint that will handle your form, you will be able to decode the base64 string to a file, or to save the base64 directly on mongoose (storing images in the db is not much recommended if a lot of images is being to be loaded, or big ones desired, as the mongoDB query will be very slow).
Hope you can solve with those indications. If you still have some doubts, please comment and I´ll try to help
I'm also a noob using MEANJS, and this is how I made it work using ng-flow + FileReader:
HTML input:
<div flow-init
flow-files-added="processFiles($files)"
flow-files-submitted="$flow.upload()"
test-chunks="false">
<!-- flow-file-error="someHandlerMethod( $file, $message, $flow )" ! need to implement-->
<div class="drop" flow-drop ng-class="dropClass">
<span class="btn btn-default" flow-btn>Upload File</span>
<span class="btn btn-default" flow-btn flow-directory ng-show="$flow.supportDirectory">Upload Folder</span>
<b>OR</b>
Drag And Drop your file here
</div>
controller:
$scope.uploadedImage = 0;
// PREPARE FILE FOR UPLOAD
$scope.processFiles = function(flow){
var reader = new FileReader();
reader.onload = function(event) {
$scope.uploadedImage = event.target.result;
};
reader.onerror = function(event) {
console.error('File could not be read! Code ' + event.target.error.code);
};
reader.readAsDataURL(flow[0].file);
};
And on the server side the variable on the model receiving the value of uploadedImage is just of type string.
Fetching it back from the server didn't require any conversion:
<img src={{result.picture}} class="pic-image" alt="Pic"/>
Now just need to find out what to do with big files...

Resources