-1

I've got a dropzone.js widget embedded in a webpage served by a node/express app. That all works famously, so I'm adding in the conditionals that provide upload constraints. There are three such constraints; mime type (that one works fine), uploaded file size (also works fine) and number of files (which does not work fine).

Here's a snippet from the code:

var fileSize = req.file.size;
if(fileSize > 200000) {
  return res.status(422).json({
    error: "File too large."
  });
}

var numfiles = fs_filecount(appwd+"data/free/req.session.freeFolderID");
if(numfiles>20) {
  return res.status(422).json({
    error: "Maximum file count reached."
  });
}

I present these two assignments and conditionals like this because this is how they appear in the .js file. The assignment and conditional for the file size works great, but that for the file count does not. The source for the fs_filecount function follows:

fs_filecount = function(path) {
  fs.readdir(path,function(err, files){
    if(err){
      return console.error(err);
    } else {
      return files.length;
    }
  });
};

Note that logging the value returned by this file count function directly demonstrates that it is working fine.

The failing is that after the assignment of numfiles, it is undefined, so the file count test always fails, and the constraint of no more than 20 files is never enforced.

EDIT: Simplified subroutine 'fs_filecount' follows:

function fs_filecount(path) {
  var count;
  fs.readdir(path,function(err,files) {
    if(err) throw err;
    else {
      count = files.length;
    }
  });
  return count;
};
  • `fs.readdir` is an _asnyc_ function, so the callback you pass to `readdir` is called **after** the exit of `fs_filecount`, so you cannot use a `return` statement to return the result of `readdir` from `fs_filecount`. You either need to use a Promise as return value or also a callback for `fs_filecount`. – t.niese Mar 18 '17 at 12:48

1 Answers1

1

fs.readdir is an async function, so the callback you pass to readdir is called after the exit of fs_filecount, so you cannot use a return statement to return the result of readdir from fs_filecount.

You either need to use a Promise as return value or use a callback for fs_filecount.

A Promise based solution would look like this

function fs_filecount(path) {
  return new Promise((resolve, reject) => {
    fs.readdir(path, (err, files) => {
      if (err) {
        reject(err)
      } else {
        resolve(files.length)
      }
    })
  })
}

And how to use it:

fs_filecount(appwd + "data/free/req.session.freeFolderID")
  .then(numfiles => {
    if (numfiles > 20) {
      return res.status(422).json({
        error: "Maximum file count reached."
      })
    }
  })
  .catch(err => {
    console.log('there was an error: ' + err)
  })

For more details check take a look at this question: How do I return the response from an asynchronous call?

Community
  • 1
  • 1
t.niese
  • 39,256
  • 9
  • 74
  • 101
  • You got there before me. Serves me right for writing code on a phone! Only spotted when I got on the pc that it is an asyn function and returns weren't going to cut it. – Conor Gallagher Mar 18 '17 at 12:59
  • 1
    @ConorGallagher if a function takes a callback and where one (in general the first) argument of the callback is the error, then you always have to assume that it is async, because a sync function could throw the error instead of passing it to the callback. – t.niese Mar 18 '17 at 13:08