Search code examples
javascriptnode.jsexpressmulter

Send uploaded Image in Express


Have an Express application that uploads an image using Multer. What I would like to be able to do is serve that image. The challenge though is when the file is uploaded it does not save the file extension which is by designed from what I've read for security purposes. What I need to be able to do is add the correct extensions to the corresponding mime type.

Technology

  • Express: 4.15.0
  • Multer: 1.3.0
  • mmmagic: 0.4.5

File Structure

root
  server.js
  /uploads
    /images
      ad58c400e67362118ac1b11f6c4b6c44
      ...
  /routes
    drinks.routes.js
    uploads.routes.js

Running this all through Postman. Have form elements name, tagline, image. If I upload "IMG_111.jpg" That image will get uploaded through Multer, validated that it's an image type, and placed in /images/uploads with a new name, default behavior for Multer to create uniqueness. The problem is I have the filename but it is not saved with an extension.

drinks.routes.js

var upload = multer({
  dest: 'uploads/images',
  fileFilter: function (req, file, callback) {
    console.log('running upload file filter');
      var fileType = file.mimetype;
      if(fileType !== 'image/png' && fileType !== 'image/jpeg' && fileType !== 'image/gif' && fileType !== 'image/jpg') {
          return callback(new Error('Only images are allowed'))
      }
      callback(null, true)
  },
}).single('image');

router.route('/').post(passport.authenticate('jwt', { session: false }), upload, function(req, res, next){
var drink = req.body;
var drinkImage = req.file;
console.log('DRINK IMAGE', drinkImage);

Drink.createDrink(drink, drinkImage, function(err, drink, drinkImage){
  console.log('creating drink');
  if(err){
    res.send(err);
  }
    res.json(drink)
  });
});

So I can upload a file, validate the file, save the file with a unique name and no extension. Where I'm have difficulties is then checking the mime type of the requested image, attaching an extension and sending that back in a response.

My original thought is to create a route with the image name requested, use mmmagic to detect the mime type, add the extension and serve that file. But, all attempts at this have failed.

uploads.routes.js

var express = require('express');
var router = express.Router();
var path = require('path');

var mmm = require('mmmagic');
var Magic = mmm.Magic;
var magic = new Magic(mmm.MAGIC_MIME_TYPE);

router.use(function(req, res, next){
  next();
});

router.route('/images/:filename')
  .get(function(req,res){
    var fileName = req.params.filename;
    var constructFile = function(res){
      magic.detectFile(__dirname + '/../uploads/images/' + fileName, function(err, result) {
        if (err) throw err;
        console.log(result);
        if (result == 'image/jpeg'){
      // return res.sendFile(__dirname + '/../uploads/images/' + fileName + '.jpg');
      res.send(express.static(path.join(__dirname, '/../uploads/images'), {index: false, extensions:['jpg']}));
    }
  });
}

constructFile(res);
  })


module.exports = router;

Tried following this SO Which lead me to express.static. But can't figure out at this point if I'm on the right track and just missing something or if I should be looking at this from a different angle.


Solution

  • It's not the cleanest implementation but it does work. Took the advice off the Multer #170 issue.

    uploads.routes.js

    router.route('/images/:filename')
      .get(function(req,res){
        var fileName = req.params.filename;
        function getFileType(file, done){
          console.log('GETTING FILE TYPE');
          magic.detectFile(__dirname + '/../uploads/images/' + fileName, function(err, result) {
            if (err) return done(err);
    
            res.setHeader('Content-Type', result)
            fs.createReadStream(path.join(__dirname + '/../uploads/images/', req.params.filename)).pipe(res)
        });
      }
      getFileType();
    })
    

    The key part that was missing was the:

    res.setHeader('Content-Type', result)
    fs.createReadStream(path.join(__dirname + '/../uploads/images/', req.params.filename)).pipe(res) 
    

    So now can upload a file, validate the mime type, store as a unique name, and finally call the file name, get the check the mime type, set the content-type to the mime type and deliver to the client.