3

I am using a directive for AngularJS called angular-file-upload. I have set it up and the image upload works fine, however I can't seem to parse the file to the server side (express js).

Jade:

input.form-input(ng-model="userAvatarFile" ng-file-select="onFileSelect($files)" name="userAvatarFile" type="file")

This is the code for AngularJS:

$scope.onFileSelect = function($files) {

    console.log($files);

    for (var i = 0; i < $files.length; i++) {
        var file = $files[i];
        $scope.upload = $upload.upload({
            url: '/api/user/upload', 
            method: 'POST',
            data: {myObj: $scope.userAvatarFile},
            file: file, 
        }).progress(function(evt) {
            console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
        }).success(function(data, status, headers, config) {
            console.log(data);
        });
    }
};

And express:

exports.upload = function(req, res) {
  console.log(req.body);
  console.log(req.files);
};

The console always prints out the following for express:

 {}
 undefined

Any ideas what might be wrong?

2
  • upload() function is an Async .. you can use it inside the loop like this , you have send all file in the array and save. Commented Jul 21, 2014 at 18:40
  • I don't quite understand what you are trying to say, can you reword it? Commented Jul 21, 2014 at 18:42

2 Answers 2

1

Multipart middleware was removed in Connect 3.0. Connect recommends either connect-multiparty or connect-busboy. IMHO connect-busboy is better, so here's how to do it:

(Of course, run npm install --save connect-busboy)

// Main file, stays the same for all the other examples
var express = require('express'),
    busboy  = require('connect-busboy');

app.use(busboy({ immediate: true }));

// Route file
exports.upload = function (req, res) {
    req.busboy.on('file', function (fieldname, file, filename) {
        // fieldname will be 'file', file will be... um... the file (in binary encoding), filename will be the file's name
    });
    req.busboy.on('field', function (key, value) {
        // key will be 'myObj', value will be $scope.userAvatarFile
    });
};

If you want to save the image to a file, you can do this:

exports.upload = function (req, res) {
    req.busboy.on('file', function (fieldname, file, filename) {
        // saveTo is the temporary path
        var saveTo = path.join(os.tmpDir(), path.basename(fieldname));
        file.pipe(fs.createWriteStream(saveTo));

        // Do stuff with saveTo
    });
};

However, this is a security risk (saving to the disk, which is why req.files does not exist anymore), so most(?) libraries that work with files (e.g. Cloudinary) provide a WriteStream (or something that looks and feels like one). Here's how it works with Cloudinary:

exports.upload = function (req, res) {
    req.busboy.on('file', function (fieldname, file, filename) {
        var stream = cloudinary.uploader.upload_stream(function(result) { console.log(result); });

        // You should have been able to do file.pipe(stream), but Cloudinary's stream isn't a real WriteStream, but just an object with two functions. Well, the following works too:

        file.on('data', stream.write);
        file.on('end', stream.end);
    });
};

FWIW, I copied your code and tested it and it works just fine. Let me know if you've got any problems. I had the same issue a week ago, so I got to look into it.

Sign up to request clarification or add additional context in comments.

Comments

1

tgkokk's example using Cloudinary no longer works with Cloudinary v1.1.1. Cloudinary updated the upload_stream method to return a Transform stream object. So use 'pipe' instead of manually adding events to on('data') and on('end').

https://github.com/cloudinary/cloudinary_npm#version-11-upload_stream-change-notes

exports.upload = function (req, res) {
    req.busboy.on('file', function (fieldname, file, filename) {
        var stream = cloudinary.uploader.upload_stream(function(result) {    console.log(result); 
        });

        // pipe can be used now
        file.pipe(stream);
    });
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.