Node.js TypeError: Cannot read property 'file' of undefined
Asked Answered
W

3

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.

Whitesell answered 3/2, 2016 at 4:29 Comment(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.Chord
You var tempPath = req.file.path; var ext = path.extname(req.file.originalname).toLowerCase(); var targetPath = path.resolve('./public/upload/' + imgUrl + ext);Improvisator
S
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.

Sibylsibylla answered 3/2, 2016 at 4:45 Comment(2)
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?Whitesell
@Whitesell 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. )Sibylsibylla
S
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

Sorayasorb answered 7/5, 2020 at 23:21 Comment(0)
N
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>
Nanna answered 15/10, 2021 at 5:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.