Question

I'm trying to upload a image from a AngularJS interface to a nodejs server (expressjs). (I'm using mean.io)

Every time I upload someting, req.body logs "{}" and req.files logs "undefined"

I'm using angular-file-upload directive in AngularJS

Client-side code:

$scope.onFileSelect = function() {
    console.log($files);
    for (var i = 0; i < $files.length; i++) {
      var file = $files[i];
      $scope.upload = $upload.upload({
        url: 'map/set', 
        method: 'POST',
        headers: {'enctype': 'multipart/form-data'},
        data: {myObj: $scope.myModelObj},
        file: file,

      }).progress(function(evt) {
        console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
      }).success(function(data, status, headers, config) {
        // file is uploaded successfully
        console.log(data);
      });
    }
  };

Server-side code

var app = express();
require(appPath + '/server/config/express')(app, passport, db);
app.use(bodyParser({uploadDir:'./uploads'}));
app.post('/map/set', function(req, res) {
    console.log(req.body);
        console.log(req.files);
    res.end('Success'); 
});

*****Edit*****

HTML Code

<div class="row">
    <input id="file" type="file" ng-file-select="onFileSelect()" >
</div>

Hand built request

$scope.onFileSelect = function() {
    //$files: an array of files selected, each file has name, size, and type.
    //console.log($files);

    var xhr = new XMLHttpRequest();
    // not yet supported in most browsers, some examples use
    // this but it's not safe.
    // var fd = document.getElementById('upload').getFormData();

    var fd = new FormData();
    var files = document.getElementById('myfileinput').files;
    console.log(files);
    for(var i = 0;i<files.length; i++) {
        fd.append("file", files[i]);
    }

    /* event listeners */
    xhr.upload.addEventListener("progress", uploadProgress, false);
    xhr.addEventListener("error", uploadFailed, false);
    xhr.addEventListener("load", uploadComplete, false);
    xhr.addEventListener("abort", uploadCanceled, false);

    function uploadComplete(){
        console.log("complete");
    }

    function uploadProgress(){
        console.log("progress");
    }

    function uploadFailed(){
        console.log("failed");
    }

    function uploadCanceled(){
        console.log("canceled");
    }

    xhr.open("POST", "map/set");

    xhr.send(fd);


  };
Was it helpful?

Solution

The latest version of mean.io uncluding express 4.x as dependency. In the documentation for migration express 3 to 4 you can read, express will no longer user the connect middlewares. Read more about here: https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x The new body-parser module only handles urlencoded and json bodies. That means for multipart bodies (file uploads) you need an additional module like busboy or formadible. Here is an example how I use angular-file-upload with busboy: The AngularJS Stuff:

$upload.upload({
  url: '/api/office/imageUpload',
  data: {},
  file: $scope.$files
}) …

I write a little helper module to handle uploads with busboy easier. It’s not very clean coded, but do the work:

var env = process.env.NODE_ENV || 'development';
var Busboy = require('busboy'),
    os = require('os'),
    path = require('path'),
    config = require('../config/config')[env],
    fs = require('fs');

// TODO: implement file size limit

exports.processFileUpload = function(req, allowedExtensions, callback){
  var busboy = new Busboy({ headers: req.headers });
  var tempFile = '';
  var fileExtenstion = '';
  var formPayload = {};

  busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
    fileExtenstion = path.extname(filename).toLowerCase();
    tempFile = path.join(os.tmpDir(), path.basename(fieldname)+fileExtenstion);
    file.pipe(fs.createWriteStream(tempFile));
  });

  busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) {
    var jsonValue = '';

    try {
      jsonValue = JSON.parse(val);
    } catch (e) {
      jsonValue = val;
    }

    formPayload[fieldname] = jsonValue;
  });

  busboy.on('finish', function() {
    if(allowedExtensions.length > 0){
      if(allowedExtensions.indexOf(fileExtenstion) == -1) {
        callback({message: 'extension_not_allowed'}, tempFile, formPayload);
      } else {
        callback(null, tempFile, formPayload)
      }
    } else {
      callback(null, tempFile, formPayload)
    }
  });

  return req.pipe(busboy);
}

In my controller i can use the module that way:

var uploader = require('../helper/uploader'),
    path = require('path');


exports.uploadEmployeeImage = function(req,res){    
    uploader.processFileUpload(req, ['.jpg', '.jpeg', '.png'], function(uploadError, tempPath, formPayload){

    var fileExtenstion = path.extname(tempPath).toLowerCase();
    var targetPath = "/exampleUploadDir/testFile" + fileExtenstion;

    fs.rename(tempPath, targetPath, function(error) {
      if(error){
        return callback("cant upload employee image");
      }

      callback(null, newFileName);
    });
  });
}

OTHER TIPS

I'm going to take a guess here that the header settings are incorrect.

   headers: {'enctype': 'multipart/form-data'},

Should be changed to:

   headers: {'Content-Type': 'multipart/form-data'},

Ensure you have an 'id' AND 'name' attribute on the file input - not having an id attribute can cause problems on some browsers. Also, try building the request like this:

    var xhr = new XMLHttpRequest();
    // not yet supported in most browsers, some examples use
    // this but it's not safe.
    // var fd = document.getElementById('upload').getFormData();

    var fd = new FormData();
    var files = document.getElementById('myfileinput').files;
    for(var i = 0;i<files.length; i++) {
        fd.append("file", files[i]);
    }

    /* event listeners */
    xhr.upload.addEventListener("progress", uploadProgress, false);
    xhr.addEventListener("error", uploadFailed, false);
    xhr.addEventListener("load", uploadComplete, false);
    xhr.addEventListener("abort", uploadCanceled, false);
    xhr.open("POST", "your/url");

    xhr.send(fd);

angular isn't great with file uploads so doing it by hand might help.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top