How to create a Web based File Browser using NodeJS, Express and JQuery Datatables

This week i released a NodeJS module file-browser.  file-browser is a nodejs utility to quickly create a HTTP based file share on your machine.  The module is available for download from npmjs at link https://www.npmjs.org/package/file-browser, and code is available on github https://github.com/sumitchawla/file-browser

How to install
npm -g install file-browser
How to Run

Change directory to the directory you want to browse. Then run the following command in that directory.

  file-browser

You would see the message Please open the link in your browser http://:8088 in your console. Now you can point your browser to your IP. For localhost access the files over http://127.0.0.1:8088

file-browser supports following command line switches for additional functinality.

    -p, --port <port>        Port to run the file-browser. Default value is 8088
    -e, --exclude <exclude>  File extensions to exclude. To exclude multiple extension pass -e multiple times. e.g. ( -e .js -e .cs -e .swp)
Screenshot
file-browser

Code 

Lets’s start by exploring the Server side NodeJS code first.

We start by creating an express app.  Then we add the current working directory as the public directory for the app.  This is the directory where you are running the command currently.  Adding the current working directory as a public directory makes all the files accessible for browsing.  Next, we add the module directory as a public directory.  This is the directory that will be containing all client side javascript and css files for our app.

var app = express();
var dir =  process.cwd();
app.use(express.static(dir)); //current working directory
app.use(express.static(__dirname)); //module directory
var server = http.createServer(app);

Next step is to create a files api in our app.  This is the api which would return the directory listing for either the top directory, or the requested directory.  API will returns metadata about all the files in the request directory.  This metadata will contain file name, file path and IsDirectory flag to differentiate files from directories.  For files we would also return file extension.  This will be used later to render file specific icons.

app.get('/files', function(req, res) {
 var currentDir =  dir;
 var query = req.query.path || '';
 if (query) currentDir = path.join(dir, query);
 console.log("browsing ", currentDir);
 fs.readdir(currentDir, function (err, files) {
     if (err) {
        throw err;
      }
      var data = [];
      files
      .forEach(function (file) {
        try {
                //console.log("processing ", file);
                var isDirectory = fs.statSync(path.join(currentDir,file)).isDirectory();
                if (isDirectory) {
                  data.push({ Name : file, IsDirectory: true, Path : path.join(query, file)  });
                } else {
                  var ext = path.extname(file);
                  if(program.exclude && _.contains(program.exclude, ext)) {
                    console.log("excluding file ", file);
                    return;
                  }       
                  data.push({ Name : file, Ext : ext, IsDirectory: false, Path : path.join(query, file) });
                }

        } catch(e) {
          console.log(e); 
        }        
        
      });
      data = _.sortBy(data, function(f) { return f.Name });
      res.json(data);
  });
});

Now lets get on the client side code. We would be creating a template.html file inside our lib directory.  This will be the main html page that will control all the client side interaction for our app. We start by including a set of bootstrap and font awesome css, and an app.css to control the layout of our app.  Similarly, we will be including a set of javascript files from our lib directory, and an app.js file. app.js includes the main interaction logic our our app, and is covered in next section.

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>File Browser</title>
    <link rel="stylesheet" href="/lib/bootstrap.min.css">
    <link rel="stylesheet" href="/lib/font-awesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="/lib/app.css">
  </head>
  <body>
   <div class="panel panel-default mainpanel">
           <div class="panel-heading">
                   File Browser
                   <span class="up">
                    <i class="fa fa-level-up"></i> Up
                   </span> 
           </div>
      <div class="panel-body">
              <table class="linksholder">
              </table>
      </div>

  </div> 
    <script src="/lib/jquery.min.js"></script>
    <script src="/lib/bootstrap.min.js"></script>
    <script src="/lib/datatable/js/jquery.datatables.min.js"></script>
    <script src="/lib/app.js"></script>
  </body>
</html>

This html will be returned on hitting the home url of the app. Let’s add a server side code to do the same.

app.get('/', function(req, res) {
 res.redirect('lib/template.html'); 
});

app.js  is responsible for interacting with the server, and controling all the rendering logic of the app.  We start by creating a simple jQuery dataTable with default options on the HTMl element linksholder.  As soon as the app is loaded we will hit the /files api to get directory listing for the app directory, and will populate the datatable with the returned json array of metadata information about the files.

var table = $(".linksholder").dataTable();

  $.get('/files').then(function(data){
      table.fnClearTable();
      table.fnAddData(data);
  });

So we got the data from the server, and we have populated the datatable with it. Now lets work on some beautification of the datatable.  To control the UI, we need to modify the dataTable contructor call and pass it some options to control its UI.

First of all we would add following aoColumns option to control the table headers and the type of data that will be rendered in the datatable.  We will be rendering only one column in our datatable.  If the data row is  a directory, then we will use a fa-folder icon with the directory name.  If the data row is a file, then we will try to find a font-awesome icon for file type using the ext value from the metadata.  We will be also be adding a link to download the file from the server. File download path is controlled by data.Path.  This value contains the full relative path w.r.t to the startup directory of the app.

"aoColumns": [
          { "sTitle": "", "mData": null, "bSortable": false, "sClass": "head0", "sWidth": "55px",
            "render": function (data, type, row, meta) {
              if (data.IsDirectory) {
                return "<a href='#' target='_blank'><i class='fa fa-folder'></i>&nbsp;" + data.Name +"</a>";
              } else {
                return "<a href='/" + data.Path + "' target='_blank'><i class='fa " + getFileIcon(data.Ext) + "'></i>&nbsp;" + data.Name +"</a>";
              }
            }
          }
        ]

Second option we will be using is do some special consideration of directory rows.  Whenever a directory name is clicked, we would need to get the content of those directory from server.  To handle that we would need to attach following click handler for directory rows.

"fnCreatedRow" :  function( nRow, aData, iDataIndex ) {
          if (!aData.IsDirectory) return;
          var path = aData.Path;
          $(nRow).bind("click", function(e){
             $.get('/files?path='+ path).then(function(data){
              table.fnClearTable();
              table.fnAddData(data);
              currentPath = path;
            });
            e.preventDefault();
          });
        },


Finally lets create a app.css file and add some css rules for beautification of the app.

body { left:10%;width:80%;position:absolute; top:10%; color: black!important;}

.mainpanel { height:90%;min-height:400px;}

.linksholder ul { list-style-type: none; }

.linksholder li { font-size: 16px; line-height: 1.5em;}

.linksholder li .fa { margin-right: 5px; }

.linksholder a { color : black;}

.linksholder .fa { color: black;}

.dataTables_filter label { float: right; font-weight:normal; position: absolute; top : 10px; right: 5px; }

.dataTables_info { margin-top: 10px; background: ghostwhite; }

.up { margin-left: 20px; border:solid 1px black; cursor:pointer; width:100px; padding-left: 10px; padding-right:10px; }

Other AngularJS Articles

Error: [ng:cpws] Can’t copy! Making copies of Window or Scope instances is not supported.

AngularJS: Override default exception handler

4 thoughts on “How to create a Web based File Browser using NodeJS, Express and JQuery Datatables

  1. I am using this code, precisely as it is. Unfortunately, I keep receiving this error, which I believe hinders the development of the database onto HTML: TypeError: e[i] is undefined

    Like

Leave a comment