One of the side projects I'm currently working on is a quick online ONIX for Books file validator utility. The tool is built using Node.js and Express, and I quickly ran into an issue with handling file uploads.

Searching Google didn't help all that much, particularly when it comes to Express 4.0, which no longer includes the Connect middleware that handles this kind of thing out of the box. After a little bit of digging, I came across the multer middleware package.

To add multer to your Express project, use npm to install it as follows (the --save flag will add the dependecy to your package.json file).

npm install multer --save

Next, you need to tell your Express app to use multer. In your app.js file, add a require as follows:

var multer = require('multer');

Next, add an app.use call somewhere above the module.exports = app line in the same file:

app.use(multer({dest:'./uploads/'}));

This will tell multer to store any uploaded files in the uploads subdirectory in your project.

With multer added to your app, you can now handle uploads in your views. My index route for the ONIX validator tool is as follows (stored in routes/index.js):

router.get('/', function(req, res) {
  res.render('index', { title: 'ONIX Validator' });
});

This renders a view named index.jade in the views directory. This view has a form with an file input element which allows the end user to select a file to upload to the server. For the upload to work, the <form> element needs to have the enctype attribute set to multipart/form-data. The following is a snippet from my index.jade file that illustrates this:

form(role="form", action="/", method="post", enctype="multipart/form-data")
  div(class="form-group")
    label(for="onixfile") Upload ONIX File        
    input(type="file", name="onixfile", id="onixfile")

This form will send a POST request to the / route. In this route, you can now access the properties of the file that has been uploaded using req.files as follows:

router.post('/', function(req, res) {
  console.dir(req.files);
});

This will output an object including various information about the file that was uploaded.

{ 
  onixfile: { 
    fieldname: 'onixfile',
    originalname: 'onix.xml',
    name: '4f67055ab5ee865df8c1ca2b1bfd2083.xml',
    encoding: '7bit',
    mimetype: 'text/xml',
    path: 'uploads/4f67055ab5ee865df8c1ca2b1bfd2083.xml',
    extension: 'xml' 
  } 
}

The most important property to take note of here is the path property, which indicates where the uploaded file has been stored on the server.

WARNING When you have finished processing the uploaded file you should delete it from the filesystem. Otherwise an attacker could easily write a script that will bombard your server with file uploads in an attempt to fill its hard disk and bring the server down. If you need to store the file, consider storing it in a purpose-built file store such as Amazon S3, or adding security measures around the upload page.