10

I'm just learning node.js and have difficulty to make a simple file upload using express and multer.

Here is the form:

Upload Image

In my configure.js I have:

app.use(express.static(path.join(__dirname, 'public')));
app.use(multer({dest:'../public/upload/temp'}).single('file'));

And the image.js controller:

create: function(req, res) {
        var saveImage = function() {
            console.log(req.body);
            var possible = 'abcdefghijklmnopqrstuvwxyz0123456789',
                imgUrl = '';

            for(var i=0; i < 6; i+=1) {
                imgUrl += possible.charAt(Math.floor(Math.random() * possible.length));
            }

            var tempPath = req.files.file.path, //<line 55 error
                ext = path.extname(req.files.file.name).toLowerCase(),
                targetPath = path.resolve('./public/upload/' + imgUrl + ext);

            if (ext === '.png' || ext === '.jpg' || ext === '.jpeg' || ext === '.gif') {


                fs.rename(tempPath, targetPath, function(err) {
                    if (err) throw err;

                    res.redirect('/images/' + imgUrl);
                });
            } else {
                fs.unlink(tempPath, function () {
                    if (err) throw err;

                    res.json(500, {error: 'Only image files are allowed.'});
                });
            }
        };

        saveImage();
    },

However I get this error when I try to upload an image:

TypeError: Cannot read property 'file' of undefined
    at saveImage (/home/pc/node-dev/test-proj/controllers/image.js:55:37)
    at module.exports.create (/home/pc/node-dev/test-proj/controllers/image.js:76:9)
    at Layer.handle [as handle_request] (/home/pc/node-dev/test-proj/node_modules/express/lib/router/layer.js:95:5)
    at next (/home/pc/node-dev/test-proj/node_modules/express/lib/router/route.js:131:13)
    at Route.dispatch (/home/pc/node-dev/test-proj/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/home/pc/node-dev/test-proj/node_modules/express/lib/router/layer.js:95:5)
    at /home/pc/node-dev/test-proj/node_modules/express/lib/router/index.js:277:22
    at Function.process_params (/home/pc/node-dev/test-proj/node_modules/express/lib/router/index.js:330:12)
    at next (/home/pc/node-dev/test-proj/node_modules/express/lib/router/index.js:271:10)
    at urlencodedParser (/home/pc/node-dev/test-proj/node_modules/body-parser/lib/types/urlencoded.js:95:37)

And when I log the req object, file is not there:

{ title: 'myimage', description: 'something' }

Actually the snippet is just a slightly modified version what I read in this book, which is using outdated express-3. So basically I just updated it with the multer part.

I'm wondering what is wrong here and how to fix it.

2
  • Did you ever find a workable solution to this with multer? I tried the fix listed below, and changing it to req.file and req.path seems to work, but now the example from the book redirects to the /image/ route without successfully naming or receiving the file. Commented Feb 9, 2016 at 21:45
  • You var tempPath = req.file.path; var ext = path.extname(req.file.originalname).toLowerCase(); var targetPath = path.resolve('./public/upload/' + imgUrl + ext); Commented Dec 6, 2016 at 6:29

3 Answers 3

13

You are using upload.single, which you should use req.file not req.files. To upload multiple files, use upload.array.

Notice that you don't need another .file after req.file. req.file is the uploaded file if you are using upload.single.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the tip. However now that I use tempPath = req.file.file.path, I causes TypeError: Cannot read property 'path' of undefined error. Any ideas?
@qliq as stated in document, you just need to use req.file.path, not req.file.file.path. ( From document: The single file will be stored in req.file. )
0

From the same book referenced above, I was able to come up with a working solution after logging to the console the req.files.file and from the details in the console the path exists in the file object and it is tempFilePath not path so the actual path would be var tempPath = req.files.file.tempFilePath;. And the full code

create: function(req, res){
    var saveImage = function(){
      var possible = 'abcdefghijklmnopqrstuvwxyz0123456789';
      var imgUrl = '';

      for(var i=0; i<6; i++){
        imgUrl += possible.charAt(Math.floor(Math.random()*possible.length));
      }

     // if (!req.file) return console.log('Please upload a file')
     console.log(req.files.file)
     //
      var tempPath = req.files.file.tempFilePath;
      var ext = path.extname(req.files.file.name).toLowerCase();
      var targetPath = path.resolve('./public/upload/' + imgUrl + ext);

      if(ext === '.png' || ext === '.jpg' || ext === '.jpeg' || ext === '.gif'){
        fs.rename(tempPath, targetPath, function(err){
          if(err) throw err;

          res.redirect('/images/' +imgUrl);
        });
      } else {
        fs.unlink(tempPath, function(){
          if(err) throw err;

          res.json(500, {error: 'Only image files are allowed'});
        })
      }
    }
    saveImage();
  }

The details to the console after logging the req.files.file

Comments

0

From the same book referenced above, this is 2021 and all codes above fail, I have battled with this for over 4 days and finally cracked it. I moved the multer middleware into the routes module for it to work

update routes.js to

    var express = require('express'),
 path = require('path'),
 router = express.Router(),
 home = require('../controllers/home'),
 image = require('../controllers/image');
 const multer  = require('multer')
const upload = multer({ dest: path.join(__dirname,
    'public/upload/temp')});
module.exports = function(app) {
 router.get('/', home.index);
 router.get('/images/:image_id', image.index);
 router.post('/images',upload.single('image'), image.create);
 router.post('/images/:image_id/like', image.like);
 router.post('/images/:image_id/comment', image.comment);
 app.use(router);
};

update the create function in the image controller to

create: function (req, res) {
    var saveImage = function () {
      var possible = "abcdefghijklmnopqrstuvwxyz0123456789",
        imgUrl = "";
      for (var i = 0; i < 6; i += 1) {
        imgUrl += possible.charAt(Math.floor(Math.random() * possible.length));
      }

      var tempPath = req.file.path,
        ext = path.extname(req.file.originalname).toLowerCase(),
        targetPath = path.resolve("./public/upload/" + imgUrl + ext);
      if (
        ext === ".png" ||
        ext === ".jpg" ||
        ext === ".jpeg" ||
        ext === ".gif"
      ) {
        fs.rename(tempPath, targetPath, function (err) {
          if (err) throw err;
          res.redirect("/images/" + imgUrl);
        });
      } else {
        fs.unlink(tempPath, function () {
          if (err) throw err;
          res.json(500, { error: "Only image files are allowed." });
        });
      }
    };
    saveImage();
  }

comment out or remove the multer middleware from the configure.js module

//app.use(multer({ dest: path.join(__dirname, 'public/upload/temp')}).single('image'));

ensure your form in the index.handlebars correspond to this

<form method="post" action="/images" enctype="multipart/form-data" >
        <div class="panel-body form-horizontal">
            <div class="form-group col-md-12">
                <label class="col-sm-2 control-label" for="file">Browse:</label>
                <div class="col-md-10">
                    <input class="form-control" type="file" name="image" id="file">
                </div>
            </div>
            <div class="form-group col-md-12">
                <label class="col-md-2 control-label" for="title">Title:</label>
                <div class="col-md-10">
                    <input class="form-control" type="text" name="title">
                </div>
            </div>
            <div class="form-group col-md-12">
                <label class="col-md-2 control-label" for="description">Description:</label>
                <div class="col-md-10">
                    <textarea class="form-control" name="description" rows="2"></textarea>
                </div>
            </div>
            <div class="form-group col-md-12">
                <div class="col-md-12 text-right">
                    <button type="submit" id="login-btn" class="btn btn-success" type="button">
                        <i class="fa fa-cloud-upload ">
                        </i> Upload Image</button>
                </div>
            </div>
        </div>
    </form>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.