Skip to content

Instantly share code, notes, and snippets.

@nagasudhirpulla
Last active June 26, 2025 01:59
Show Gist options
  • Save nagasudhirpulla/c9b0fc8974aac99aa664be17b32edf7b to your computer and use it in GitHub Desktop.
Save nagasudhirpulla/c9b0fc8974aac99aa664be17b32edf7b to your computer and use it in GitHub Desktop.
Directory Browsing with sorting and additional columns in IIS

Directory browsing with sorting in IIS

  • Place the Files.aspx and Files.aspx.cs files in a folder which needs to be served as directory browsing in IIS
  • Make Files.aspx as the default document for the website in IIS
  • Files can be downloaded via static files IIS feature. Hence, enable the static content and ASP.NET latest version in "Turn Windows features on or off"
  • Request Filtering in IIS can be used for additional security measures like limiting file extensions, file size, url size etc.
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Files.aspx.cs" Inherits="Files.FilesPage" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="Files" %>
<!DOCTYPE html>
<html>
<head>
<title>Contents of <%= path %></title>
<style type="text/css">
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
p {
font-family: verdana;
font-size: 10pt;
}
h2 {
font-family: verdana;
}
td, th {
font-family: verdana;
font-size: 10pt;
padding-left: 1em;
}
th {
text-align: left;
text-decoration: underline;
}
</style>
</head>
<body>
<h2>
<asp:HyperLink runat="server" ID="NavigateUpLink">[To Parent Directory]</asp:HyperLink>
<%= path %>
</h2>
<hr />
<table>
<tr>
<th>Name</th>
<th>Last Modified</th>
<th>Size (KB)</th>
<th>Extension</th>
<th>Type</th>
</tr>
<% foreach (DirectoryListingEntry fItem in listing)
{ %>
<tr>
<td>
<% if (fItem.FileSystemInfo is DirectoryInfo)
{%>
<a href="?path=<%=fItem.VirtualPath%>"><%=fItem.Filename%></a>
<%}
else
{%>
<a href="<%=fItem.VirtualPath%>" target="_blank"><%=fItem.Filename%></a>
<%}%>
</td>
<td><%= fItem.FileSystemInfo.LastWriteTime.ToString("yyyy-MMM-dd HH:mm")%></td>
<td><%= GetFileSizeString(fItem.FileSystemInfo) %></td>
<td><%=fItem.FileSystemInfo.Extension %></td>
<td><%=(fItem.FileSystemInfo is DirectoryInfo)?"folder":"file" %></td>
</tr>
<% } %>
</table>
<hr />
<p>
<asp:Label runat="Server" ID="FileCount" />
</p>
<script>
// https://stackoverflow.com/questions/14267781/sorting-html-table-with-javascript
var getCellValue = function (tr, idx) { return tr.children[idx].innerText || tr.children[idx].textContent; }
var comparer = function (idx, asc) {
return function (a, b) {
return function (v1, v2) {
return v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2) ? v1 - v2 : v1.toString().localeCompare(v2);
}(getCellValue(asc ? a : b, idx), getCellValue(asc ? b : a, idx));
}
};
// setup table sorting by clicking column header
Array.prototype.slice.call(document.querySelectorAll('th')).forEach(function (th) {
th.addEventListener('click', function () {
var table = th.parentNode
while (table.tagName.toUpperCase() != 'TABLE') table = table.parentNode;
Array.prototype.slice.call(table.querySelectorAll('tr:nth-child(n+2)'))
.sort(comparer(Array.prototype.slice.call(th.parentNode.children).indexOf(th), this.asc = !this.asc))
.forEach(function (tr) { table.appendChild(tr) });
})
});
</script>
</body>
</html>
using System;
using System.IO;
using System.Web;
using System.Collections.Generic;
namespace Files
{
public partial class FilesPage : System.Web.UI.Page
{
public DirectoryListingEntryCollection listing = new DirectoryListingEntryCollection();
public string path = "/";
protected void Page_Load()
{
path = Request.QueryString["path"];
if (string.IsNullOrEmpty(path))
{
path = "/";
}
else
{
path = VirtualPathUtility.AppendTrailingSlash(path);
}
string sortBy = Request.QueryString["sortby"];
//
// Databind to the directory listing
//
DirectoryInfo dirInfo = new DirectoryInfo(Server.MapPath(path));
FileSystemInfo[] fiFiles = dirInfo.GetFileSystemInfos();
foreach (FileSystemInfo f in fiFiles)
{
if (f.Attributes.HasFlag(FileAttributes.Hidden)) { continue; }
DirectoryListingEntry e = new DirectoryListingEntry();
e.FileSystemInfo = f;
e.Filename = f.Name;
//e.Path = f.FullName;
e.VirtualPath = path + (path.EndsWith("/") ? "" : "/") + f.Name;
listing.Add(e);
}
if (listing == null)
{
throw new Exception("This page cannot be used without the DirectoryListing module");
}
//
// Handle sorting
//
if (string.IsNullOrEmpty(sortBy))
{
// set default sorting
sortBy = "daterev";
}
if (sortBy.Equals("name"))
{
listing.Sort(DirectoryListingEntry.CompareFileNames);
}
else if (sortBy.Equals("namerev"))
{
listing.Sort(DirectoryListingEntry.CompareFileNamesReverse);
}
else if (sortBy.Equals("date"))
{
listing.Sort(DirectoryListingEntry.CompareDatesModified);
}
else if (sortBy.Equals("daterev"))
{
listing.Sort(DirectoryListingEntry.CompareDatesModifiedReverse);
}
else if (sortBy.Equals("size"))
{
listing.Sort(DirectoryListingEntry.CompareFileSizes);
}
else if (sortBy.Equals("sizerev"))
{
listing.Sort(DirectoryListingEntry.CompareFileSizesReverse);
}
//
// Prepare the file counter label
//
FileCount.Text = listing.Count + " items";
//
//
// Parepare the parent path label
string parentPath = null;
if (path.Equals("/") || path.Equals(VirtualPathUtility.AppendTrailingSlash(HttpRuntime.AppDomainAppVirtualPath)))
{
// cannot exit above the site root or application root
parentPath = null;
}
else
{
parentPath = VirtualPathUtility.Combine(path, "..");
}
if (string.IsNullOrEmpty(parentPath))
{
NavigateUpLink.Visible = false;
NavigateUpLink.Enabled = false;
}
else
{
NavigateUpLink.NavigateUrl = "?path=" + parentPath;
}
}
public string GetFileSizeString(FileSystemInfo info)
{
if (info is FileInfo)
{
return string.Format("{0}", (int)(((FileInfo)info).Length * 10 / (double)1024) / (double)10);
}
else
{
return string.Empty;
}
}
}
public class DirectoryListingEntryCollection : List<DirectoryListingEntry>
{
}
public class DirectoryListingEntry
{
//public string Path;
public string VirtualPath;
public string Filename;
public FileSystemInfo FileSystemInfo;
internal static int CompareFileNames(DirectoryListingEntry x, DirectoryListingEntry y)
{
return x.Filename.CompareTo(y.Filename);
}
internal static int CompareFileNamesReverse(DirectoryListingEntry x, DirectoryListingEntry y)
{
return -1 * x.Filename.CompareTo(y.Filename);
}
internal static int CompareDatesModified(DirectoryListingEntry x, DirectoryListingEntry y)
{
return x.FileSystemInfo.LastWriteTime.CompareTo(y.FileSystemInfo.LastWriteTime);
}
internal static int CompareDatesModifiedReverse(DirectoryListingEntry x, DirectoryListingEntry y)
{
return -1 * x.FileSystemInfo.LastWriteTime.CompareTo(y.FileSystemInfo.LastWriteTime);
}
internal static int CompareFileSizes(DirectoryListingEntry x, DirectoryListingEntry y)
{
if (x.FileSystemInfo is DirectoryInfo)
{
return -1;
}
if (y.FileSystemInfo is DirectoryInfo)
{
return 1;
}
return ((FileInfo)x.FileSystemInfo).Length.CompareTo(((FileInfo)y.FileSystemInfo).Length);
}
internal static int CompareFileSizesReverse(DirectoryListingEntry x, DirectoryListingEntry y)
{
if (x.FileSystemInfo is DirectoryInfo)
{
return -1;
}
if (y.FileSystemInfo is DirectoryInfo)
{
return 1;
}
return -1 * ((FileInfo)x.FileSystemInfo).Length.CompareTo(((FileInfo)y.FileSystemInfo).Length);
}
}
}
@grester
Copy link

grester commented Jun 25, 2025

It's only showing web.config, I've added the file name extension to the request filtering, mime type, directory browsing and disabled file compression but still doesn't show anything else.
I'm trying on a virtual directory of a site. I've tried setting the path var to the directory path but no success.
Win Server 22 Std

Update: Yes it's only loading site root folder not the virtual directory itself as root, so it can't find any files.

@nagasudhirpulla
Copy link
Author

Hi @grester, please see this blog here. I have made a video also on this at here
I did not consider virtual host in this blog, but I think it should not be a problem. Please comment back and share ur experience on how you resolved this.
Cheers 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment