I am trying to upload multipart/form-data for a simple MEAN stack application. When putting everything in one file and running it it works fine.
Server.js
var express = require('express');
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
switch(file.mimetype) {
case 'image/jpg' :
case 'image/jpeg':
case 'image/gif':
var extension = file.mimetype.split("/");
extension = extension[extension.length-1];
break
case 'video/quicktime':
var extension = 'mov';
break
case 'video/mp4':
var extension = 'mp4';
break
default:
var extension = 'jpeg';
}
cb(null, file.fieldname + '-' + Date.now() + "." + extension);
}
});
var upload = multer({ storage: storage });
var path = require('path');
var app = express();
var segmentUpload = upload.fields([{ name: 'segmentVideo', maxCount: 1}, { name: 'segmentStill', maxCount: 1}, { name: 'segmentGif', maxCount: 1}])
app.post('/photos/upload', segmentUpload, function (req, res, next) {
console.log(req);
console.log(req.files['segmentVideo'][0]);
console.log(req.files['segmentStill'][0]);
console.log(req.files['segmentGif'][0]);
console.log(req.body.title);
});
app.get('/', function(req, res) {
res.sendfile('./index.html');
});
app.listen(8080);
console.log("App listening on port 8080");
index.html
<body>
<form action="/photos/upload" method="post" enctype="multipart/form-data">
title: <input type="text" name="title"> <br><br>
Select video to upload:
<input type="file" name="segmentVideo" id="fileToUpload"> <br><br>
Select jpeg to upload:
<input type="file" name="segmentStill" id="fileToUpload"><br><br>
Select gif to upload:
<input type="file" name="segmentGif" id="fileToUpload"><br><br>
<input type="submit" value="Upload Image" name="submit">
</form>
But when I try to integrate multer into my node routing I cannot get my app to accept the multipart/form-data.
app/routes.js
var Video = require('./models/video');
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
switch(file.mimetype) {
case 'image/jpg' :
case 'image/jpeg':
case 'image/gif':
var extension = file.mimetype.split("/");
extension = extension[extension.length-1];
break
case 'video/quicktime':
var extension = 'mov';
break
case 'video/mp4':
var extension = 'mp4';
break
default:
var extension = 'jpeg';
}
console.log("new extension: " + extension);
cb(null, file.fieldname + '-' + Date.now() + "." + extension);
}
});
var upload = multer({ storage: storage });
module.exports = function(app) {
// api --------------------------------------
// get all todos
app.get('/api/videos', function(req,res){
// use mongoose to get all todos in the database
Video.find(function(err, videos){
// if there is an error, send the error
if (err)
res.send(err);
res.json(videos);
});
});
var segmentUpload = upload.fields([{ name: 'segmentVideo', maxCount: 1}, { name: 'segmentStill', maxCount: 1}, { name: 'segmentGif', maxCount: 1}])
app.post('/api/videos', segmentUpload, function(req, res){
// create a video
Video.create({
title : req.body.title,
description : req.body.description,
category : req.body.category,
day : req.body.day,
videoUrl : req.body.videoUrl,
stillUrl : req.body.stillUrl,
gifUrl : req.body.gifUrl,
airReady : false
}, function(err, video) {
if (err)
res.send(err)
// get and return all the todos after you create another
Video.find(function(err, videos){
if (err)
res.send(err)
res.json(videos);
});
});
});
// delete a todo
app.delete('/api/videos/:video_id', function(req,res){
Video.remove({
_id : req.params.video_id
}, function(err, video) {
if (err)
res.send(err);
// get and return all the todos after you delete one
Video.find(function(err, videos) {
if (err)
res.send(err)
res.json(videos);
});
});
});
// edit a todo
app.put('/api/videos/:video_id', function(req,res){
Video.findByIdAndUpdate(req.params.video_id,
{ $set: { title: req.body.title
}},
function(err, video){
if (err)
res.send(err)
// get and return all todos after edit
Video.find(function(err, videos){
if (err)
res.send(err)
res.json(videos);
});
});
});
// find -------------------------------------------------------------
app.get('*', function(req, res) {
res.sendfile('./public/index.html'); // load the single view file (angular will handle the page changes on the front-end)
});
};
I receive an error:
TypeError: Cannot read property 'title' of undefined
This is my first post to stack overflow, but I appreciate all of the support!
The problem might be due to use are sending the multipart/form-data but you are not converting it to json and you are using json for retrieving the data.
Use body-parser
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.json());
Related
I have a form with text-fields & drop-downs.
I am using Node.js , Angular js, Express, MongoDb.
I want to post data along with image(s).
I want to store image in folder, without any base/binary conversion, and image path should be stored in mongoDb.
How can I achieve this?
HTML FILE
<div class="form-group">
<label class="col-md-4">Profile Photo</label>
<input type="file" class="form-control"
formControlName="s_profile"
#s_profile ng2FileSelect
[uploader]="uploader"
(change)="uploader.uploadAll()" />
</div>
<div class="form-group">
<button (click)="addfile()"
[disabled]="angForm.pristine || angForm.invalid"
class="btn btn-primary">Upload</button>
</div>
COMPONENT FILE
const URL = 'http://localhost:4000/api/upload';
public uploader: FileUploader = new FileUploader({url: URL, itemAlias: 'photo'});
ngOnInit() {
this.uploader.onAfterAddingFile = (file) => { file.withCredentials = false; };
this.uploader.onCompleteItem = (item: any, response: any, status: any, headers: any) => {
//console.log('ImageUpload:uploaded:', item, status, response);
console.log(response);
this.profilPhotos = response;
};
}
addfile(){
this.ss.addStudent(this.profilPhotos);
}
SERVICE FILE
uri = 'http://localhost:4000/business';
addBusiness(uploadImages) {
const obj = {uploadImages: uploadImages};
this.http.post(`${this.uri}/add`, obj)
.subscribe(res => console.log('Done'));
}
SERVER FILE
const DIR = '../src/assets/upload';
let storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR);
},
filename: (req, file, cb) => {
console.log(file);
cb(null, file.originalname);
}
});
let upload = multer({storage: storage});
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');
res.setHeader('Access-Control-Allow-Methods', 'POST, GET');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.setHeader('Access-Control-Allow-Credentials', true);
next();
});
app.post('/api/upload',upload.single('angular'), function (req, res) {
if (!req.file) {
console.log("No file received");
return res.send({
success: false
});
} else {
console.log('file received');
return res.send(req.file.filename)
}
});
ROUTER FILE
const path = require('path');
const fs = require('fs');
const express = require('express');
const multer = require('multer');
const bodyParser = require('body-parser');
const app = express();
const router = express.Router();
const DIR = './uploads';
let storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR);
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now() + '.' + path.extname(file.originalname));
}
});
let upload = multer({storage: storage});
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');
res.setHeader('Access-Control-Allow-Methods', 'POST');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.setHeader('Access-Control-Allow-Credentials', true);
next();
});
app.post('/api/upload',upload.single('photo'), function (req, res) {
if (!req.file) {
return res.send({
success: false
});
} else {
return res.send({
return res.send(req.file.filename)
})
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, function () {
console.log('Node.js server is running on port ' + PORT);
});
You can use the simple/lightweight ng-file-upload directive. It supports drag&drop.
<div ng-controller="MyCtrl">
<input type="file" ngf-select="onFileSelect($files)" multiple>
</div>
JS:
angular.module('myApp', ['ngFileUpload']);
var MyCtrl = [ '$scope', 'Upload', function($scope, Upload) {
$scope.onFileSelect = function($files) {
Upload.upload({
url: 'api/upload',
method: 'POST',
file: $files,
}).progress(function(e) {
}).then(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
});
}];
You can use formidable for parsing form data, especially file uploads. Read more here
app.post('/api/upload', function(req, res) {
var form = new formidable.IncomingForm();
form.multiples = true;
form.uploadDir = path.join(__dirname, '../../public/images');
form.on('file', function(field, file) {
//copy file to new path
fs.renameSync(file.path, path.join(form.uploadDir, file.name));
var Image = path.join(form.uploadDir, file.name);
//save new path to mongodb
db.collection('images').insert({
imagePath: Image
}, function(err, result) {
// do your stuff
})
});
})
This is just my suggestion. You should learn more and do what you expect. Hope it help.
Front-End:
HTML:
<input type="file" nv-file-select uploader="vm.uploader"
id="fileUpload" ng-model="vm.schema.file"/>Browse
Controller:
angular
.module('myApp')
.controller('myController', myController);
myController.$inject = ['FileUploader'];
function EditSchemaBasicController(FileUploader) {
vm.uploader = new FileUploader({
url: "https://localhost:3000/api/file", //your api URL
queueLimit: 10,
onAfterAddingFile: function(item) {
//before upload logic will go here like file size and extension handling
item.upload();
},
onCompleteItem: function(item, response){
//on complete
console.log('Uploaded File: ' + response.file);
},
onErrorItem: function(item, response) {
//error handling
}
});
}
Back-End:
'use strict'
var express = require('express');
var router = express.Router();
var fs = require('fs-extra');
//Post file.. i used busboy to upload the file
router.post('/', function(req, res, next) {
req.pipe(req.busboy);
req.busboy.on('file', function (fieldname, file, filename) {
filename = decodeURI(filename);
//Path where file will be uploaded
var fstream = fs.createWriteStream('API/Images' + filename);
file.pipe(fstream);
fstream.on('close', function () {
res.status(201).json({file: filename});
});
});
});
module.exports = router;
I would like to send a file to my Node.js application, but it seems that the application receives nothing. I'm not sure what to do. How am I supposed to check If I have sent the file, and if I am receiving it in req?
<form>
<input type = "file" file-model="files" multiple/>
<button class="md-primary md-button md-cyan-theme md-ink-ripple" ng-click = "vm.uploadFile()">upload me</button>
</form>
This is my controller:
function uploadFile() {
console.log("Load");
var fd = new FormData();
console.log($scope.files) // FileList {0: File, Length: 1}
angular.forEach($scope.files, function (file) {
fd.append('file', file);
});
console.log(fd); // FormData {} (Empty?)
$http.post('http://localhost:8090/file-upload'), {
headers: {'Content-Type': undefined },
files: fd
}).success(function (d) {
console.log(d);
});
}
This is my directive:
.directive('fileMode', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind('change', function () {
$parse(attrs.fileModel).assign(scope, elemtn[0].files);
scope.$apply();
});
}
};
}])
Here is my Express app:
app.post('/file-upload', function (req, res, next) {
console.log("Sent!");
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './uploads');
},
filename: function (req, file, cb) {
cb(null, file.fieldname);
}
});
var upload = multer({ storage : Storage }).array('userPhoto', 2);
upload(req, res, function (err) {
console.log(req.body.data.files);
if (err) return res.end("Error uploading file.");
res.end("File is uploaded.");
})
})
Thank you for the help.
The field names are not the same in web-form and in multer configuration:
var upload = multer({ storage : storage }).array('file',2);
Hope this will work!
var express = require('express'),
app = express(),
bodyParser = require('body-parser'),
multer = require('multer');
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "http://localhost");
res.header(
"Access-Control-Allow-Origin",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.use(express.static('../client'));
app.use(bodyParser.json());
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads/');
},
filename: function (req, file, cb) {
var split = file.originalname.split('.');
cb(null, file.fieldname + "-" + Date.now() + "." + split[split.length - 1]);
}
});
var upload = multer({ storage : storage }).single('file');
app.post('/upload', function (req, res) {
upload(req, res, function (err) {
if (err) res.json({ error_code: 1, err_desc: err });
res.json({error_code: 0, err_desc: null });
})
})
app.listen('3000', function () {
console.log("Running on 3000");
})
I'm using NodeJS, ANgularJS, and MongoDB with mongoose to make a website. I'm having some trouble adding an object in mongoDB. The name of the object is Todo.
Here are the models of Todo:
var mongoose = require('mongoose');
var TodoSchema = new mongoose.Schema({
name: String,
password : String,
completed: Boolean,
note: String
});
module.exports = mongoose.model('Todo', TodoSchema);
In the controller I create a new Todo and push it
angular.module('app').controller('Connexion', ['$scope', 'Todos','$location', function($scope,Todos, $location) {
$scope.editing = [];
$scope.todos = Todos.query();
$scope.save = function() {
var todo = new Todos({ name: "test", password: "test", completed: false });
$scope.todos.push(todo);
Todos.save($scope.todo);
}
}]);
This is my html page, each time I click on the button I Have a new todo created and it's displayed on screen:
<button ng-click="save()">Creer POST </button>
<ul>
<li ng-repeat="todo in todos">
{{todo.name}}
{{todo.password}}
</li>
</ul>
But I have a problem, the new object is not added on the database. How can I do it?
This is my files in the back-end :
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var Todo = require('../models/Todo.js');
/* GET /todos listing. */
router.get('/', function(req, res, next) {
Todo.find(function (err, todos) {
if (err) return next(err);
res.json(todos);
});
});
/* POST /todos */
router.post('/', function(req, res, next) {
Todo.create(req.body, function (err, post) {
if (err) return next(err);
res.json(post);
});
});
/* GET /todos/id */
router.get('/:id', function(req, res, next) {
Todo.findById(req.params.id, function (err, post) {
if (err) return next(err);
res.json(post);
});
});
/* PUT /todos/:id */
router.put('/:id', function(req, res, next) {
Todo.findByIdAndUpdate(req.params.id, req.body, function (err, post) {
if (err) return next(err);
res.json(post);
});
});
/* DELETE /todos/:id */
router.delete('/:id', function(req, res, next) {
Todo.findByIdAndRemove(req.params.id, req.body, function (err, post) {
if (err) return next(err);
res.json(post);
});
});
module.exports = router;
And if this can help this the back-end file app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var mongo = require('mongodb');
var Post = require('./models/Post.js');
var routes = require('./routes/index');
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/todoApp', function(err) {
if(err) {
console.log('connection error', err);
} else {
console.log('connection successful');
}
});
var app = express();
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', function(req, res, next){
res.sendFile(__dirname + '/public/index.html');
});
var found = ['DB Connection not yet established. Try again later. Check the console output for error messages if this persists.'];
module.exports = app;
$scope.todo from Todos.save($scope.todo); is not defined so this is why you might not manage to save anything
The following code produces an image in the desired location, however, the image is not being named correctly.
routes.js
var upload = multer({
dest: './client/shared/assets/images/logos',
rename: function (fieldname, filename, req, res) {
console.log('Rename YO!')
console.log(fieldname)
console.log(filename)
console.log(req)
return 'test' + '-'+Date.now();
}
});
app.post('/uploads/logo',upload.single('file'), function(req, res){
console.log('Upload photo yo!')
console.log(req.body) // form fields
console.log(req.file) // form files
res.status(204).end()
});
create.controller.js
$scope.saveLogo = function(file) {
console.log(file);
file.upload = Upload.upload({
url: '/uploads/logo',
method: 'POST',
fields: {
name: $scope.item.name,
subdomain: $scope.item.subdomain
}
});
file.upload.then(function (_res) {
console.log(_res)
file.result = _res.data;
}, function (_res) {
console.log(_res)
if (_res.status > 0)
$scope.errorMsg = _res.status + ': ' + _res.data;
});
file.upload.progress(function (evt) {
// Math.min is to fix IE which reports 200% sometimes
file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
});
};
I don't know what I am missing here. I am trying to limit the multer functionality to a specific post action, could that be it?
Thanks in advance
Solved with this little tidbit:
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage })
The approach has changed to the above method since the last release
I am trying to get the data from my form into my hapijs server. I don't seem to be managing. When I submit the form, the data is passed as undefined which trigers an error on the server. From what I understand hapi parses the data automatically.
Could someone please help me understand what I am doing wrong? Why am I getting undefined?
The function that handles the form data is sendworkout.
These are my routes:
var path = require('path');
var _ = require('underscore');
var couchbase = require('couchbase');
//Connect to database.
var db = db || new couchbase.Connection({host: 'localhost:8091', bucket: 'default'}, function(err) {
if (err) {
console.log('Connection Error', err);
} else {
console.log('Connected!');
}
});
console.log(db);
//We have a pending connection to the test database running on localhost.
//We now need to get notified if we connect successfully or if a connection error occurs
var landingPage = {
handler: function(req, reply) {
reply.file('index.html');
}
};
var getWorkouts = {
handler: function (req, reply) {
// set options for databse query
var q ={
descending: true,
stale: false
};
// show multiple exercises - db.view(designDocument, viewName, options)
db.view('workout', 'exercise', q).query(function(err, values){
// use pluck method from underscore to retrieve data
var keys = _.pluck(values, 'id');
console.log("Keys: " + keys);
//fetch multiple documents based on the 'keys' object
db.getMulti(keys, null, function(err, results){
console.log('Results: ' + results);
var workouts = [];
for (var prop in results) {
workouts.push(results[prop].value);
}
reply(workouts);
});
});
}
};
var getMusclegroup = {
handler: function (req, reply) {
var q = {
descending: true,
stale: false
};
db.view('workout', 'exercise', q).query(function(err, values){
var keys = _.pluck(values, 'id');
db.getMulti(keys, null, function(err, results){
var muscleGroups = [];
for (var prop in results) {
console.log(typeof results);
console.log(results[prop].value.workout);
muscleGroups.push(results[prop].value.workout);
}
reply(muscleGroups[0]);
});
});
}
};
var sendWorkout = {
handler: function(req, reply){
var d = new Date();
var cd = d.getDate() + "-" + (d.getMonth()+1) + "-" + d.getFullYear();
console.log(req.method); // getting a post method - OK
console.log(req.body); // returns undefined
// defines unique key for data
var key = cd;
console.log(key);
// adds payload to database
db.add(key, req.body, function(error, results){
if (error) {
console.log("Coushbase error: " + error);
reply(error + "\n");
}
console.log(results);
reply(req.body);
});
}
};
var workoutNew = {
handler: function (req, reply) {
reply.file("static/html/workoutForm.html");
},
};
module.exports = [
{method: 'GET', path: '/static/{param*}', config: { handler: { directory: { path: 'static'}}}},
{method: 'GET', path: '/', config: landingPage},
{method: 'GET', path: '/workouts', config: getWorkouts},
{method: 'GET', path: '/workouts/musclegroup', config: getMusclegroup},
{method: 'GET', path: '/newworkout', config: workoutNew},
{method: 'POST', path:'/newworkout/workout', config: sendWorkout}
];
This is my server module:
var Hapi = require('hapi');
var path = require('path');
var Joi = require('joi');
var rs = require('./lib/modules/routes.js');
var config= { };
var server = Hapi.createServer(process.env.PORT || 8080, config);
server.route(rs);
server.start(function(){
console.log("Server started: " + server.info.uri);
});
module.exports = server;
This is my html form:
<div id="forms">
<form id="workout-form" name="workout-form" action="newworkout/workout" method="POST">
<div class="workouts">
<label for="exercise" class="labels">Exercise</label><input type="text" name="exercise" id="exercise" placeholder="Which exercise?" autofocus />
<label for="musclegroup" class="labels">Muscle-Group</label><input type="text" name="musclegroup" id="musclegroup" placeholder="Which muscle-group?" />
<div class="sets">
<label for="reps" class="labels">Reps</label><input type="text" name="reps" id="reps" class="reps-column" placeholder="How many reps?" />
<label for="kilos" class="labels">Kg's</label><input type="text" name="kilos" id="kilos" class="kilos-column" placeholder="How much Kg?" />
</div>
<hr>
</div>
<button id="add-set"class="add-buttons" type="button"><i class="fa fa-plus-circle fa-2x"></i></button>
<button id="add-exercise" class="add-buttons" type="button"><i class="fa fa-arrow-circle-down fa-2x"></i></button>
<button id="submit-workout" type="submit" name="submitbutton"><strong>Save Workout</strong></button>
</form>
</div>
Just replace req.body with req.payload:
var path = require('path');
var _ = require('underscore');
var couchbase = require('couchbase');
//Connect to database.
var db = db || new couchbase.Connection({host: 'localhost:8091', bucket: 'default'}, function(err) {
if (err) {
console.log('Connection Error', err);
} else {
console.log('Connected!');
}
});
console.log(db);
//We have a pending connection to the test database running on localhost.
//We now need to get notified if we connect successfully or if a connection error occurs
var landingPage = {
handler: function(req, reply) {
reply.file('index.html');
}
};
var getWorkouts = {
handler: function (req, reply) {
// set options for databse query
var q ={
descending: true,
stale: false
};
// show multiple exercises - db.view(designDocument, viewName, options)
db.view('workout', 'exercise', q).query(function(err, values){
// use pluck method from underscore to retrieve data
var keys = _.pluck(values, 'id');
console.log("Keys: " + keys);
//fetch multiple documents based on the 'keys' object
db.getMulti(keys, null, function(err, results){
console.log('Results: ' + results);
var workouts = [];
for (var prop in results) {
workouts.push(results[prop].value);
}
reply(workouts);
});
});
}
};
var getMusclegroup = {
handler: function (req, reply) {
var q = {
descending: true,
stale: false
};
db.view('workout', 'exercise', q).query(function(err, values){
var keys = _.pluck(values, 'id');
db.getMulti(keys, null, function(err, results){
var muscleGroups = [];
for (var prop in results) {
console.log(typeof results);
console.log(results[prop].value.workout);
muscleGroups.push(results[prop].value.workout);
}
reply(muscleGroups[0]);
});
});
}
};
var sendWorkout = {
handler: function(req, reply){
var d = new Date();
var cd = d.getDate() + "-" + (d.getMonth()+1) + "-" + d.getFullYear();
console.log(req.method); // getting a post method - OK
console.log(req.payload);
// defines unique key for data
var key = cd;
console.log(key);
// adds payload to database
db.add(key, req.payload, function(error, results){
if (error) {
console.log("Coushbase error: " + error);
reply(error + "\n");
}
console.log(results);
reply(req.payload);
});
}
};
var workoutNew = {
handler: function (req, reply) {
reply.file("static/html/workoutForm.html");
},
};
module.exports = [
{method: 'GET', path: '/static/{param*}', config: { handler: { directory: { path: 'static'}}}},
{method: 'GET', path: '/', config: landingPage},
{method: 'GET', path: '/workouts', config: getWorkouts},
{method: 'GET', path: '/workouts/musclegroup', config: getMusclegroup},
{method: 'GET', path: '/newworkout', config: workoutNew},
{method: 'POST', path:'/newworkout/workout', config: sendWorkout}
];