Uploading images with Mongoose, Express and AngularJS Uploading images with Mongoose, Express and AngularJS mongodb mongodb

Uploading images with Mongoose, Express and AngularJS


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;};

enter image description here

server/config/environment/development.js

enter image description here

server/api/upload/upload.controller.js

enter image description here


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