0

I am working on a small web portal, and I've run into an error when I am uploading files(images) > ~25kb. I've tested the system, and it works great with small images, but then when I try a larger image, everything breaks.

I am using the latest version of node, express 3.4.0, multer 0.0.7 and here is the code in question:

Server:

var express = require('express'),
    multer = require('multer'),
    api = require('./routes/api.js'),
    http = require('http'),
    path = require('path'),
    app = express(),
    server = http.createServer(app);


app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.set('port', process.env.VCAP_APP_PORT || 7000);
app.use(multer({dest: __dirname + '/uploads/'}))
app.use(app.router);

app.configure(function()
    app.use("/uploads",express.static(__dirname + '/uploads'));

    app.post('/api/upload/file', api.upload);

    app.use(function(err, req, res, next) {
        if(!err) return next();
        console.log(err.stack);
        res.json({error: true});
    });

    app.use(multer({
        dest:'./uploads/',
         rename: function (fieldname, filename) {
            return filename.replace(/\W+/g, '-').toLowerCase();
        }
    }));
});

server.listen(app.get('port'), function(){
    console.log('Express server listening on port ' + app.get('port'));
});

And the api.upload function:

exports.upload = function(req, res) {

    console.log("Upload");
    var resumeName = null,
        picName = null;
    console.log("File: " + JSON.stringify(req.files));
    if(req.files.resume.size >= 0 || req.files.pic.size >= 0) {
        if(req.files.resume.size > 0) {
            resumeName = req.files.resume.name;
        }
        if(req.files.pic.size > 0) {
            picName = req.files.pic.name;
        }
        console.log("Got file");
    }
    console.log("sending...");
    res.send({picName: picName, resumeName: resumeName});
}

If it helps at all, my front end is angular, and I am using restangular and ng-upload to assist with from submitting and uploading files.

mscdex
  • 104,356
  • 15
  • 192
  • 153
techfreek
  • 3
  • 3

2 Answers2

0

You are configuring your application inside app.configure()

app.configure(function()
    app.use("/uploads",express.static(__dirname + '/uploads'));

    app.post('/api/upload/file', api.upload);

    app.use(function(err, req, res, next) {
        if(!err) return next();
        console.log(err.stack);
        res.json({error: true});
    });

    app.use(multer({
        dest:'./uploads/',
         rename: function (fieldname, filename) {
            return filename.replace(/\W+/g, '-').toLowerCase();
        }
    }));
});

and outside of it.

app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.set('port', process.env.VCAP_APP_PORT || 7000);
app.use(multer({dest: __dirname + '/uploads/'}))
app.use(app.router);

Notice that you have defined the multer middleware twice.

I guess multer must be defined before app.use(app.router). The second definition is causing the sending of headers after they have been sent.

NOTE: app.configure() is deprecated and was even removed beginning in express 4.x. I do not recommend its usage. You better define all your middleware outside.

Leonel Machava
  • 1,501
  • 11
  • 19
  • I went ahead and updated to express 4.0, removed app.configure() and `app.use(multer({dest: __dirname + '/uploads/'}))`. My code still works fine for small images, and still crashes on larger ones. Maybe it's an issue with multer? I will do some more testing in the meantime. – techfreek May 28 '14 at 23:48
  • It is strange. It seems to be an issue with multer. Try https://github.com/mscdex/connect-busboy or https://github.com/andrewrk/connect-multiparty. – Leonel Machava May 29 '14 at 08:02
0

TL;DR There was (what amounted to) a race case in saving the uploads on the server. Uploading files individually fixed that.

I ended up following @Leonel's advice and moving to connect-busboy. Initially I had the same issue, then I realized what was happening. I use ng-upload in the front end, but ng-upload will upload all the files in one go. Since the form I was using had two (optional) files that a user could upload, the backend uploading service would be hit with two files in req.files. Since my implementation of multer didn't plan for this too well, it would try to handle both, but would only send one response (which ever hit the res.send line first).

To fix this, I moved to angular-file-upload which allows me to have more options over how I upload files (will upload individually). In addition, I used safwanc's solution to using connect-busboy for file uploads.

Community
  • 1
  • 1
techfreek
  • 3
  • 3