title: Uploading images with Mongoose, Express and AngularJS
body: <p>I know this has been asked many times before, and I have read almost all I could find about the subject, namely:</p>
  
  <p><a href="https://stackoverflow.com/a/25022437/1031184">https://stackoverflow.com/a/25022437/1031184</a></p>
  
  <p><a href="https://stackoverflow.com/questions/5149545/uploading-images-using-node-js-express-and-mongoose">Uploading images using Node.js, Express, and Mongoose</a></p>
  
  <p>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.</p>
  
  <p>So with that I would really love it if someone could please walk me though how to upload images using Mongoose, Express &amp; AngularJS. I am actually using the MEAN fullstack. (this generator to be precise – <a href="https://github.com/DaftMonk/generator-angular-fullstack" rel="nofollow noreferrer">https://github.com/DaftMonk/generator-angular-fullstack</a>)</p>
  
  <p>AddController:</p>
  
  <pre><code>'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();
              });
          }
  
    }]);
  </code></pre>
  
  <p>I want to add the Images ID reference to <code>project.images.thumbnail</code> but I want to store all the information inside an Image Object using the following Schema:</p>
  
  <pre><code>'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);
  </code></pre>
  
  <p>I have also added the following <a href="https://github.com/nervgh/angular-file-upload" rel="nofollow noreferrer">https://github.com/nervgh/angular-file-upload</a> to my bower packages.</p>
  
  <p>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.</p>
  
  <p>--------------------------------------------------------------------------\</p>
  
  <p><strong>UPDATE:</strong></p>
  
  <p>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.</p>
  
  <p><strong>client/app/people/add/add.controller.js</strong></p>
  
  <pre><code>'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;
          };
  
      }]);
  </code></pre>
  
  <p><strong>server/api/image/image.model.js</strong> I would like to store all image information here and use this to populate <code>profileImage</code> in people controller.</p>
  
  <pre><code>'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);
  </code></pre>
  
  <p><strong>client/app/people/add/add.jade</strong></p>
  
  <pre><code>.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 &amp;&amp; submitted,\
          "has-error": form.firstName.$invalid &amp;&amp; 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 &amp;&amp; submitted')
                  | First name is required
  
          .form-group(ng-class='{ "has-success": form.lastName.$valid &amp;&amp; submitted,\
          "has-error": form.lastName.$invalid &amp;&amp; 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 &amp;&amp; submitted')
                  | Last name is required
  
          .form-group(ng-class='{ "has-success": form.username.$valid &amp;&amp; submitted,\
          "has-error": form.username.$invalid &amp;&amp; submitted }')
              label Username
              input.form-control(type='text', name='username', ng-model='user.username', required='')
              p.help-block(ng-show='form.username.$error.required &amp;&amp; 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 &amp;&amp; submitted,\
          "has-error": form.email.$invalid &amp;&amp; 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 &amp;&amp; submitted')
                  | Doesn't look like a valid email.
              p.help-block(ng-show='form.email.$error.required &amp;&amp; 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 &amp;&amp; submitted,\
          "has-error": form.password.$invalid &amp;&amp; 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) &amp;&amp; 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
  </code></pre>
  
  <p><strong>server/api/upload/index.js</strong></p>
  
  <pre><code>'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;
  </code></pre>
  
  <p><strong>server/api/upload/upload.controller.js</strong></p>
  
  <pre><code>'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);
  }
  </code></pre>
  
  <p><strong>server/config/environment/development.js</strong></p>
  
  <pre><code>aws: {
          key: 'XXXXXXXXXXXX',
          secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
          region: 'sydney',
          bucketName: 'my-uploads'
      }
  </code></pre>