Search This Blog

2018/02/25

Express.js Uploading files using multer

Multer is a node.js middleware for handling multipart/form-data, which is primarily used for uploading files.

To demonstrate upload using multer we will first create a view say 'upload.ejs' which 'multipart/form-data' form.


<!DOCTYPE html>
<html>

<head>
  <title>
    <%= title %>
  </title>
  <link rel='stylesheet' href='/stylesheets/style.css' />
</head>

<body>
  <h1>
    <%= title %>
  </h1>
  <p>Welcome to
    <%= title %>
  </p>

  <form method="post" enctype="multipart/form-data" action="/upload" >
    <table>
      <tr>
        <td colspan="2">
          <%if(uploaded.length > 0){%>
            <p><b>Uploaded Files:</b></p>
            <%for( var i=0;i < uploaded.length;i++){%>
              <p><%=uploaded[i]%></p>
            <%}
          }%>

          <%if(message !=''){%>
            <p><b><%=message%></b></p>
          <%}%>
        </td>
      </tr>
      <tr>
        <td>Profile Pic (*.jpg|*.jpeg)</td>
        <td>
          <input type="file" name="profile_pic" />
        </td>
      </tr>
      <tr>
        <td>Text Data (*.txt) </td>
        <td>
          <input type="file" name="text_data" />
        </td>
      </tr>
      <tr>
        <td></td>
        <td>
          <input type="submit" name="Upload" />
        </td>
      </tr>
    </table>
  </form>
</body>
</html>
</pre>

Now lets take a look at app.js

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var uuid = require('uuid/v4')

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

//multer
const multer = require('multer');




var commonModules = {
  "express": express,
  "app": app,
  "multer": multer,
  'uuid':uuid
};

var route = require('./routes/index')(commonModules);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

Here we are creating a commonModule collection and passing it to route

My route file index.js is

module.exports = function (commonModules) {
  var defaultRoute = commonModules.express.Router();
  var uuid = commonModules.uuid;
  var multer = commonModules.multer;


  var storageOptions = multer.diskStorage({
    destination: function (req, file, cb) {
      if (file.fieldname == 'profile_pic' && file.mimetype == 'image/jpeg') {
        cb(null, 'uploads/profile_pic/')
      }

      if (file.fieldname == 'profile_pic' && file.mimetype != 'image/jpeg') {
        console.log('please upload valid jpeg file');
        cb({ message: 'please upload valid jpeg file' }, null);
      }

      if (file.fieldname == 'text_data' && file.mimetype == 'text/plain') {
        cb(null, 'uploads/text_data/')
      }

      if (file.fieldname == 'text_data' && file.mimetype != 'text/plain') {
        console.log('please upload valid text file');
        cb({ message: 'please upload valid text file' }, null);
      }
    },
    filename: function (req, file, cb) {
      var fileparts = file.originalname.split('.');
      var ext = fileparts[fileparts.length - 1];
      cb(null, file.fieldname + '-' + Date.now() + '.' + ext)
    }
  });

  /* GET home page. */
  defaultRoute.get('/', function (req, res, next) {
    var viewData = {
      title: 'Express',
      uploaded: []
    }
    res.render('index', viewData);
  });


  //GET
  defaultRoute.get('/upload', function (req, res, next) {
    console.log('GET:upload');

    var uploaded = [];

    var viewData = {
      title: 'Upload Demo',
      uploaded: uploaded,
      message: ''
    }
    res.render('upload', viewData);
  });

  //post
  var file_fields = [{ name: 'profile_pic', maxCount: 1 }, { name: 'text_data', maxCount: 1 }];
  var upload_fields = multer({
    storage: storageOptions,
    limits: {
      fileSize: 1000000
    }
  }).fields(file_fields);
  defaultRoute.post('/upload/field', function (req, res, next) {
    upload_fields(req, res, function (err) {
      var uploaded = [];
      var message = '';
      var viewData = {};

      if (err) {
        message = err.message;
      } else {
        uploaded.push(req.files.profile_pic[0].filename);
        uploaded.push(req.files.text_data[0].filename);
      }

      viewData = {
        title: 'Upload Demo',
        uploaded: uploaded,
        message: message
      }

      console.log(viewData);

      res.render('upload', viewData);
    });
  });

  //upload demo
  var uploadAny = multer({
    storage: storageOptions,
    limits: {
      fileSize: 1000000
    }
  }).any();
  defaultRoute.post('/upload', function (req, res, next) {
    uploadAny(req, res, function (err) {
      var uploaded = [];
      var errors = [];
      var message = '';
      var viewData = {};

      if (err) {
        message = err.message;
      } else {
        for (var i = 0; i < req.files.length; i++) {
          uploaded.push(req.files[i].filename);
        }
      }

      viewData = {
        title: 'Upload Demo',
        uploaded: uploaded,
        message: message
      }
      res.render('upload', viewData);
    })
  });

  commonModules.app.use('/', defaultRoute);
}

Our upload.ejs contain a form with two file fields 'text_data' and 'profile_pic' and form has action '/upload'.In route we are validating that 'profile_pic' contain jpeg & 'text_data' contain plain text file else it raises error.If either of file field has invalid upload then upload of both file field fails.

Created 'uploads' folder in root directory which contain two folders 'profile_pic' & 'text_data'. which contain uploaded image & uploaded text file respectively.

multer upload has been implemented in two different post routes '/upload/field' & '/upload'
The route '/upload/field' uploads using multers 'any' function while route '/upload' implements multer's 'field' functionlity.

For testing purpose visit http://localhost:3000/upload & select text file in first input type file & submit the error message will be displayed as 'please upload valid jpeg file'.
Now in second file field & first file field select and image & submit you will get error message 'please upload valid jpeg file'.

If we select image in first file field & text in second file field then files get uploaded in respective directories '/uploads/profile_pic' & '/uploads/text_data'.
 
Code for the application is available at https://github.com/gitsangramdesai/express-multer-upload.
 

No comments:

Post a Comment