1

Currently working on a small project and having a few issues with getting files to print onto the page. Currently, all files within my /views directory will print, however, I am wanting to expand this so it can print files within folders, for example, /views/test/prototype.html.

module.exports = function (router) {

  var fs = require('fs');

  router.get('/file-list', function (req, res) {

    var markup = function (files, callback) {
      var items = [];
      for (i = 0; i < files.length; i++) {
        var q = files[i];
        q = q.slice(0, -5);
        var markup = '<li>' + '<a href="' + q + '">' + q + '</a>' + '</li>';
        items.push(markup);
      };
      callback(items);
    };

    var getFiles = function (callback) {
      fs.readdir(__dirname + '/views', function (err, files) { // '/' denotes the root folder
        if (err) throw err;
        markup(files, callback)
      });
    };

    getFiles(function (items) {

      // render markup for items
      res.render('file-list', { 'files': items });
    });
  });
}
Vikrant
  • 4,920
  • 17
  • 48
  • 72
Fenwick17
  • 368
  • 1
  • 2
  • 13

2 Answers2

0

You need to examine files array with fs.stat and recursively do getFiles on directories.

This answer solves similar problem

Community
  • 1
  • 1
notgiorgi
  • 1,691
  • 12
  • 27
0

Inside your getFiles method, you can add some extra logic to see if the file that you are currently parsing is a directory, case in which you can loop over the files in that directory and push them to a result set array (filesArray). My example below only takes into account level 2 folders, such as in the /views/test/prototype.html scenario that you provided:

 module.exports = function (router) {

     var fs = require('fs');

     router.get('/file-list', function (req, res) {

         var markup = function (files, callback) {

             var items = [];
             for (i = 0; i < files.length; i++) {
                 var q = files[i];

                 q = q.slice(0, -5);
                 var markup = '<li>' + '<a href="' + q + '">' + q + '</a>' + '</li>';
                 items.push(markup);
             };
             callback(items);
         };

         var getFiles = function (callback) {

             var rootDir = __dirname + '/views';

             fs.readdir(rootDir, function (err, files) { // '/' denotes the root folder
                 if (err) throw err;

                 // Subdirectory parsing logic START
                 var filesArray = [];

                 for (var i = 0; i < files.length; i++) {
                     var subdirPath = rootDir + '/' + files[i];

                     fs.stat(subdirPath, function (err, stats) {
                         if (err) {
                             console.log(err);
                             throw err;
                         }

                         if (stats.isDirectory()) {
                             fs.readdir(subdirPath, function (err, subdirFiles) {
                                 if (err) throw err;

                                 Array.prototype.forEach.call(subdirFiles, function (file) {
                                     filesArray.push(file);
                                 });
                             });
                         } else {
                             filesArray.push(files[i]);
                         }
                     });
                 }
                 // Subdirectory parsing logic END

                 markup(filesArray, callback)
             });
         };

         getFiles(function (items) {

             // render markup for items
             res.render('file-list', {
                 'files': items
             });
         });
     });
 }

The above example is a crude one, as it would be recommended to use a mechanism such as Promises that allows you to better control the asynchronous looping going around and make sure that you have parsed all the individual files before calling markup().

UPDATE #1 I've been trying various approaches for trying to crawl all directories and subdirectories using promises, with no success, but I did manage to come across a working solution for your scenario in this Stack Overflow accepted answer - to be more precise, it's the parallel search mechanism provided in the aforementioned answer.

Below is an adaptation of the parallel search mechanism blended in with your existing code base - I've tested it and it works as intended:

var fs = require('fs');
var path = require('path');

module.exports = function (router) {
    router.get('/file-list', function (req, res) {
        var getFiles = function (callback) {
            var rootDir = path.join(__dirname, 'views');

            walk(rootDir, function (err, results) {
                if (err) {
                    console.log(err);
                    return;
                }

                markup(results, callback);
            });

        };

        var walk = function (dir, done) {
            var results = [];

            fs.readdir(dir, function (err, list) {
                if (err) {
                    return done(err);
                }

                var pending = list.length;

                if (!pending) {
                    return done(null, results);
                }

                list.forEach(function (file) {
                    file = path.resolve(dir, file);

                    fs.stat(file, function (err, stat) {
                        if (stat && stat.isDirectory()) {
                            walk(file, function (err, res) {
                                results = results.concat(res);

                                if (!--pending) {
                                    done(null, results);
                                }
                            });
                        } else {
                            results.push(file);

                            if (!--pending) {
                                done(null, results);
                            }
                        }
                    });
                });
            });
        };

        var markup = function (files, callback) {

            var items = [];
            for (i = 0; i < files.length; i++) {
                var q = files[i];

                q = q.slice(0, -5);
                var markup = '<li>' + '<a href="' + q + '">' + q + '</a>' + '</li>';
                items.push(markup);
            };
            callback(items);
        };

        getFiles(function (items) {

            // render markup for items
            res.render('file-list', {
                'files': items
            });
        });
    });
};
Community
  • 1
  • 1
vladzam
  • 5,462
  • 6
  • 30
  • 36
  • At this moment, crude is fine. As this is merely a prototype, allowing the designer to easily access and view specific pages. Unfortunately when implementing your code it causes an error. Cannot read property 'isDirectory' of undefined. Which I think is odd as from googling isDirectory it is a built in function. – Fenwick17 Sep 27 '16 at 12:16
  • @Fenwick17 I've updated the code to check for any errors that fs.stat might throw. Can you please try it again? Thanks. – vladzam Sep 27 '16 at 12:19
  • Added in the code and went to load the file-list.html page which has the nunjucks variable "files" where the markup variable should be adding the links. However on refresh it gives the error "Cannot read property 'isDirectory' of undefined". As far as I am aware I have the required packages installed just as fs. – Fenwick17 Sep 27 '16 at 12:24
  • @Fenwick17 You get that exact same error even after adding the update that I did to the code? – vladzam Sep 27 '16 at 12:32
  • Yes, that is with your updated code. Have added a console.log(rootDir) and that is correctly showing the right filepath, so that doesnt seem to be it. – Fenwick17 Sep 27 '16 at 12:34
  • After further inspection it appears that when you move down into getFiles the files[] is no longer defined. – Fenwick17 Sep 27 '16 at 12:42
  • @Fenwick17 I've updated my answer with a new mechanism that achieves what you are looking for. Also, I ran the code on my machine and everything works as intended. Can I kindly ask you to test it out and let me know if it works? Thanks. – vladzam Sep 27 '16 at 20:36
  • This appears to be working as intended. thank you very much! Been a fantastic help to me. – Fenwick17 Sep 28 '16 at 14:56