Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to list contents of a directory IN ORDER with node.js?

I'm a fairly experienced programmer and I've just recently discovered node.js. I love JavaScript because that's where I started (Web Development) so being able to write server-side code with its is amazing.

Currently, I'm working on a simple exercise, a WebSocket/HTTP server, and I began to add a directory list function when I ran into a slight annoyance: when I list directories and files in a certain directory they're not in any order. I would like them to be listed with directories first, files second than alphabetically (like the 'ls' command). I have a feeling its because its asynchronus but I'm not totally positive. Any help would be appreciated.

BTW, here's my code:

var sys = require("sys");
var ws = require('websocket-server');
var fs = require("fs");
var path = require("path");
var url = require("url");

function log(data){
  sys.log("\033[0;32m"+data+"\033[0m");
}

var server = ws.createServer();
server.listen(3400);
log("Listening on 3400 for HTTP and WS");

server.addListener("request", function(request, response){
  log("HTTP: Connected: " + request.connection.remoteAddress);
  var uri = url.parse(request.url).pathname;
  var filename = path.join("/home/brandon", uri);
  log("HTTP: " + request.connection.remoteAddress + " Requested: " + filename);
  path.exists(filename, function(exists) {
        if(!exists) {
            response.writeHead(404, {"Content-Type": "text/plain"});
            response.write("404 Not Found\n");
            log("HTTP: " + filename + " Does Not Exist. 404 returned to " + request.connection.remoteAddress);
            response.end();
            log("HTTP: Disconnected: " + request.connection.remoteAddress);
            return;
        }

        fs.readFile(filename, "binary", function(err, file) {
            if(err) {
                if(err.errno === 21){
                    fs.readdir(filename, function(err1, files){
                        if(err1){ 
                            response.writeHead(500, {"Content-Type": "text/plain"});
                            response.write("Error when reading directory: " + err1 + "\n");
                            log("HTTP: " + filename + " Could Not Be Read. 500 returned to " + request.connection.remoteAddress);
                            response.end();
                            log("HTTP: Disconnected: " + request.connection.remoteAddress);
                            return;
                        } else {
                            response.writeHead(200);
                            response.write("<HTML><HEAD><title>Directory Listing for " + uri + "</title></HEAD><BODY><h1>Directory Listing for " + uri + "</h1>");
                            response.write("<ul>");
                            function printBr(element, index, array) {
                                response.write("<li>" + element + "</li>");
                            }
                            /*for( i in files ){
                                response.write("<li>" + files[i] + "</li>");
                            }*/
                            files.forEach(printBr);
                            response.write("</ul>");
                            response.write("</BODY></HTML>");
                            log("HTTP: Directory listing for " + filename + " sent to " + request.connection.remoteAddress);
                            response.end();
                            log("HTTP: Disconnected: " + request.connection.remoteAddress);
                            return;
                        }
                    });
                    return;
                }
                response.writeHead(500, {"Content-Type": "text/plain"});
                response.write("Error when reading file: " + err + "\n");
                log("HTTP: " + filename + " Could Not Be Read. 500 returned to " + request.connection.remoteAddress);
                response.end();
                log("HTTP: Disconnected: " + request.connection.remoteAddress);
                return;
            }

            response.writeHead(200);
            response.write(file, "binary");
            log("HTTP: " + filename + " Read and Sent to " + request.connection.remoteAddress);
            response.end();
            log("HTTP: Disconnected: " + request.connection.remoteAddress);
        });
    });
});

server.addListener("connection", function(conn){
  log(conn.id + ": new connection");
  server.broadcast("New Connection: "+conn.id);
  conn.addListener("readyStateChange", function(readyState){
    log("stateChanged: "+readyState);
  });

  conn.addListener("close", function(){
    var c = this;
    log(c.id + ": Connection Closed");
    server.broadcast("Connection Closed: "+c.id);
  });

  conn.addListener("message", function(message){
    log(conn.id + ": "+JSON.stringify(message));

    server.broadcast(conn.id + ": "+message);
  });
});

And here's the output in the browser: Output in Browser

**SOLVED:**

Thanks to @Samir, I found out how to do exactly what I wanted to do. I iterated through the array of the directory contents, checked if an item was a directory or file, separated them into two arrays ('dirs_in' for dirs and 'files_in' for files), sorted the two arrays alphabetically, and finally wrote them out.

Code (Lines 42-70):

response.writeHead(200);
response.write("<HTML><HEAD><title>Directory Listing for " + uri + "</title></HEAD><BODY><h1>Directory Listing for " + uri + "</h1>");
response.write("<ul>");
function printBr(element, index, array) {
    response.write("<li>" + element);
    if( fs.statSync( path.join(filename + element) ).isDirectory() ){
        response.write(" is a <b>dir</b>");
    } else {
        response.write(" is a <b>file</b>");
    }
    response.write("</li>");
}
var dirs_in = [];
var files_in = [];
function sep(element, index, array) {
    if( fs.statSync( path.join(filename + element) ).isDirectory() ){
        dirs_in.push(element);
    } else {
        files_in.push(element);
    }
}
files.forEach(sep);
dirs_in.sort().forEach(printBr);
files_in.sort().forEach(printBr);
response.write("</ul>");
response.write("</BODY></HTML>");
log("HTTP: Directory listing for " + filename + " sent to " + request.connection.remoteAddress);
response.end();
log("HTTP: Disconnected: " + request.connection.remoteAddress);

Browser Output: Problem Solved P.S. I'll remove the 'is a dir' and 'is a file'. They were just for testing.

like image 908
Brandon Avatar asked Jun 05 '11 22:06

Brandon


People also ask

How do you list all the files in a folder in JS?

To get a list of filenames in folder with JavaScript, we use the Node. js fs. readdirSync method. const fs = require("fs"); const files = fs.

How do I read a directory in node JS?

The fs. readdir() method is used to asynchronously read the contents of a given directory. The callback of this method returns an array of all the file names in the directory. The options argument can be used to change the format in which the files are returned from the method.

How do I sort a list in node JS?

sort() is an array function from Node. js that is used to sort a given array. Parameter: This function does not takes any parameter. Return type: The function returns the sorted array.

What is __ Dirname in node?

__dirname: It is a local variable that returns the directory name of the current module. It returns the folder path of the current JavaScript file. Difference between process.cwd() vs __dirname in Node.js is as follows: process.cwd()


1 Answers

If you want them ordered by name, you can just call sort() on the array first.

files.sort().forEach(printBr);

If, for example, you'd like to sort directories first, then you need to get more information. A naive implementation would be to query the stats of each file in the sort comparison function:

files.sort(function(a, b) {
    var aIsDir = fs.statSync(dir + "/" + a).isDirectory(),
        bIsDir = fs.statSync(dir + "/" + b).isDirectory();
    
    if (aIsDir && !bIsDir) {
        return -1;
    }

    if (!aIsDir && bIsDir) {
        return 1;
    }

    return a.localeCompare(b);
}).forEach(printBr);

The localeCompare method is what the sort method uses by default as the comparison function, so we delegate to that if they're both on "equal" terms. You can expand upon this as necessary. I'd also recommend you store the result of isDirectory in a map or something at the very least. In addition, statSync is good for demonstration purposes, but not in production code. Use stat instead. This does lead to slightly more complex code, but the benefits of asynchronous behaviour are worth it.

like image 97
Samir Talwar Avatar answered Oct 25 '22 13:10

Samir Talwar