1

I'm using express node framework with multipart middleware. Setting the multipart defer: true option gets me access to req.form in the route but it does not seem to be a valid instantiation of the multiparty Form Object. It's just a plain old JS Object.

The issue is that I cannot use the features outlined in multiparty's documentation on the Form Object. Listening for events, parsing the form, etc. are not working.

I'm using this as recommended instead of the depreciated bodyParser() with express:

app.use(express.urlencoded());
app.use(express.json());
app.use(express.multipart({ limit: '900mb', defer: true }));

In my route:

app.post('/api/videos/upload', isLoggedIn, function(req, res) {
    var form = req.form;
    form.on('progress', function(bytesReceived, bytesExpected) {
        console.log(bytesReceived + ' / ' + bytesExpected);
    });

I get nothing in the logs about progress. All the examples I see assume this form object is the real deal and not just a data object. What are they doing differently? What am I missing?

I have seen solutions that suggest writing my own middleware that use multiparty directly. I considered this but I'm using the multipart middleware for other forms at different routes that do not need this level of control. Is it possible to use multipart and write my own middleware just for uploads?

In case I'm going down a rabbit hole I'll explain my ultimate goal. I want to check the expected bytes before the upload starts so I can kick it back to the front end immediately. No reason the user should wait for 900MB to upload before finding out the file is too big. If anyone has any insight on how to achieve that some other way please share.

1 Answers1

5

When you use express.multipart(), you still using the deprecated express.bodyParser() explanation here.

As a result, we have to replace it for one of the following parsers/middleware directly:

  1. formidable

  2. connect-multiparty or [multiparty]

  3. connect-busboy or busboy

I choosed the multiparty API directly.

In your route:

var multiparty = require('multiparty'),
    fs = require('fs'),
    uuid = require('node-uuid');

app.post('/api/videos/upload', function(req, res) {

  var size = '';
  var file_name = '';
  var destination_path = '';

  // Instatiation in order to use the API options multiparty.Form({options})
  var form = new multiparty.Form();

  form.on('error', function (err) {
    console.log('Error parsing form: ' + err.stack);
  });

  form.on('part', function (part) {
    if (!part.filename)
        return;
    size = part.byteCount;
    file_name = part.filename;
  });

  form.on('file', function (name, file) {
    var temporal_path = file.path;
    var extension = file.path.substring(file.path.lastIndexOf('.'));
    destination_path = './public/uploads/' + uuid.v4() + extension;
    var input_stream = fs.createReadStream(temporal_path);
    var output_stream = fs.createWriteStream(destination_path);
    input_stream.pipe(output_stream);

    input_stream.on('end',function() {
        fs.unlinkSync(temporal_path);
        console.log('Uploaded : ', file_name, size / 1024 | 0, 'kb', file.path, destination_path);
    });
  });

  form.on('close', function(){
    res.end('Uploaded!');
  });

  form.parse(req);

});

What are you using in your front-end?. In order to get the size of the file before upload it, you could use AngularJS for instantaneous double way data binding. There is a nice angular-file-upload module that can works in conjunction with the above implementation. However, if you want to write your own bodyParser for multipart/form-data you can overwrite it as following:

require('express').bodyParser.parse['multipart/form-data'] = function yourHandler(req, options, next)...
Community
  • 1
  • 1
German Blanco
  • 816
  • 6
  • 9