1

I have a simple upload application written in NodeJS using Multer, which works fine. Here's the code:

var express = require('express'),
    bodyParser = require('body-parser'),
    qs = require('querystring'),
    multer = require('multer'),
    logger = require('morgan');

var config = require('./config'),
    oauth = require('./oauth');

function extractExtension(filename) {
    return filename.split('.').pop();
}

function getRandom(min, max) {
    return Math.floor(Math.random() * max) + min;
}


var app = express();
//app.use(cors());

// Add headers
app.use(function(req, res, next) {

    // Website you wish to allow to connect
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');

    // Request methods you wish to allow
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');

    // Request headers you wish to allow
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,Authorization,content-type');

    // Set to true if you need the website to include cookies in the requests sent
    // to the API (e.g. in case you use sessions)
    res.setHeader('Access-Control-Allow-Credentials', true);

    // Pass to next layer of middleware
    next();
});

// Multer
var momentUpload = multer({
    dest: './uploads/',
    limits: {
        fileSize: 256 * 1024 * 1024
    },
    rename: function(fieldname, filename) {
        return Date.now() + '-' + getRandom(100000, 999999) + '.' + extractExtension(filename);
    },
    onFileUploadStart: function(file) {
        console.log(file.originalname + ' is starting ...')
    },
    onFileUploadComplete: function(file) {
        console.log(file.fieldname + ' uploaded to  ' + file.path)
    }
}).single('file');

app.set('port', 4000);
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
    extended: true
}));

app.post('/file/upload', [oauth.ensureAuthenticated, momentUpload, function(req, res) {
    console.log(req.body); // form fields
    console.log(req.file); // form files

    res.status(204).end();
}]);

// Start the Server
app.listen(app.get('port'), function() {
    console.log('Metadata store env: ' + config.METADATASTORE_ENV);

    console.log('Express server listening on port ' + app.get('port'));

    firebase.authenticate();
    console.log('Connected to Firebase');
});

The problem is, however, that the configuration of Multer doesn't seem to be working at all. The destPath works, and files appear in the folder I provided (./uploads/). Larger file sizes are allowed (e.g. a file of 400MB, while the options clearly state 256MB), and the callback functions aren't fired once. There is no error message. Any idea what I'm doing wrong here? I followed guides on Google and on the official page, but can't get it to work.

Flock Dawson
  • 1,842
  • 4
  • 22
  • 34

1 Answers1

2

First of all, multer has changed its API recently, so it no longer accepts rename, onFileUploadStart or onFileUploadComplete!

We can take a look at the API here https://github.com/expressjs/multer, so lets analyse the new way things work!

NOTE: If you haven't updated your multer version, I strongly advise you to, because older versions are suspected to have a security breach.

Basic Usage

Multer accepts an options object, the most basic of which is the dest property, which tells Multer where to upload the files. In case you omit the options object, the files will be kept in memory and never written to disk.

By default, Multer will rename the files so as to avoid naming conflicts. The renaming function can be customized according to your needs. The options object also accepts fileFilter(a function to control which files are uploaded) and limits(an object that specifies the size limits) parameters.

So, your code would look like this (concerning only the multer part and considering you'd want to use all options which you don't have to):

// Multer
var momentUpload = multer({
    dest: './uploads/',
    limits: {
        fileSize: 256 * 1024 * 1024
    },
    fileFilter: function(req, file, cb) {

        // The function should call `cb` with a boolean
        // to indicate if the file should be accepted

        // To reject this file pass `false`, like so:
        cb(null, false)

        // To accept the file pass `true`, like so:
        cb(null, true)

        // You can always pass an error if something goes wrong:
        cb(new Error('I don\'t have a clue!'))

    }
}).single('file');

If you want to have more control of the storing of files you can use a storage engine. You can create your own or simply use the ones available.

The available ones are: diskStorage to store files on disk or memoryStorage to store files in the memory as Buffer objects.

Since you clearly want to store files in the disk, let's talk about the diskStorage.

There are two options available: destination and filename.

destination is used to determine within which folder the uploaded files should be stored. This can also be given as a string (e.g. '/tmp/uploads'). If no destination is given, the operating system's default directory for temporary files is used.

Note: You are responsible for creating the directory when providing destination as a function. When passing a string, multer will make sure that the directory is created for you.

filename is used to determine what the file should be named inside the folder. If no filename is given, each file will be given a random name that doesn't include any file extension.

So, your code (concerning only the multer part) would look like this:

// Multer
//Storage configuration
var storageOpts = multer.diskStorage({
    destination: function (req, file, cb) {
        //Choose a destination
        var dest = './uploads/';

        //If you want to ensure that the directory exists and 
        //if it doesn't, it is created you can use the fs module
        //If you use the following line don't forget to require the fs module!!!
        fs.ensureDirSync(dest);
        cb(null, dest);
    },
    filename: function (req, file, cb) {

        //here you can use what you want to specify the file name
        //(fieldname, originalname, encoding, mimetype, size, destination, filename, etc)
        cb(null, file.originalname);
    }
});

var momentUpload = multer({
    storage: storageOpts,
    limits: {
        fileSize: 256 * 1024 * 1024
    },
    fileFilter: function(req, file, cb) {

        // The function should call `cb` with a boolean
        // to indicate if the file should be accepted

        // To reject this file pass `false`, like so:
        cb(null, false)

        // To accept the file pass `true`, like so:
        cb(null, true)

        // You can always pass an error if something goes wrong:
        cb(new Error('I don\'t have a clue!'))

   }
}).single('file');

Hope it helped! :)

ana.arede
  • 698
  • 7
  • 12