Singe File ASPX File Browser
            
HTML            
            
Website            
            
jQuery            
            
DataTables            
    
     
                            
                                Put the .aspx file in a folder on your website and it will show all the files in a folder like a file browser. The file table uses Bootstrap 5 to show the files in a table.
                                
                                
                                When you use the filename "Default.aspx", you can show the contents of a folder directly like www.yourwebsite.com/files, but you can rename it to like "Browser.aspx". Then the url will be www.yourwebsite.com/files/browser.aspx
                                
                                
                                Comes with some configurable options like:
                                
- Enable / disable the display and browsing of subfolders
- View the files in a DataTable.
- Enable / disable the Breadcrumb path or table footer.
- Easily changable texts to your own language.
- Specify which files are allowed and which folders are blocked.
Options
| Option | Description | Default | 
|---|---|---|
| BrowseSubFolders | Allows the browsing of subfolders. | True | 
| ShowBreadCrumbs | Show the Breadcrumb path above the file table. | True | 
| ShowFooter | Show the footer with a summary of size and number of files. | True | 
| ShowSizeInMb | Shows the size of files in MB instead of KB. | False | 
| UseDataTables | Use DataTable for column sorting, searching and more. | True | 
| UseDataTablesSearch | Enables easy searching of items in the file table. | True | 
| AllowedFileTypes | Edit this list to add or remove allowed file types to be shown in the file table. | |
| BlockedFolders | Edit this list to add or remove folders from the blocked list. | 
Live demo
Code Snippets
<%@ Page Language="C#" %>
<%@ Import Namespace="System.IO" %>
<script runat="server" type="text/C#">
    //variables for various options
    bool BrowseSubFolders = true;
    bool ShowBreadCrumbs = true;
    bool ShowFooter = true;
    bool ShowSizeInMb = false;
    bool UseDataTables = true;
    bool UseDataTablesSearch = true;
    //localizable texts
    string TxtColumnFileName = "FileName";
    string TxtColumnDate = "Date";
    string TxtColumnType = "Type";
    string TxtColumnSize = "Size";
    string TxtFolderUp = ". . .";
    string TxtRoot = "root";
    string TxtCurrentFolder = "Current folder";
    string TxtFooter = "{0:N0} files found with a total size of {1:N1} {2}.";
    string TxtDatatableEmpty = "No files found in the current folder.";
    string TxtDatatableSearch = "Search";
    string TxtDatatableNohits = "No matching files found.";
    string TxtSizeKb = "kb";
    string TxtSizeMb = "mb";
    //allowed file types
    List<string> AllowedFileTypes = new List<string>()
    {
        ".ai",
        ".bmp",
        ".eps",
        ".gif",
        ".jpg",
        ".jpeg",
        ".png",
        ".psd",
        ".svg",
        ".7z",
        ".csv",
        ".doc",
        ".docx",
        ".flac",
        ".mp3",
        ".mp4",
        ".pdf",
        ".ppt",
        ".pptx",
        ".rar",
        ".txt",
        ".xls",
        ".xlsx",
        ".xml",
        ".zip"
    };
    //blocked folder names
    List<string> BlockedFolders = new List<string>()
    {
        "bin"
    };
    //some variables
    string RootFolder;
    string RootFolderNav;
    string CurrentFolder;
    string ParentFolder;
    string DownloadLinkPrefix;
    decimal SizeDivider;
    bool IsSubFolder;
    List<FileInfo> AllFiles;
    List<DirectoryInfo> AllFolders;
    //executed on page load
    protected void Page_Load(object sender, EventArgs e)
    {
        string RequestPath = Request.CurrentExecutionFilePath;
        string QueryString = "";
        //find the root folder of the browser
        RootFolder = RequestPath.Replace(RequestPath.Split('/').Last(), "").TrimEnd('/');
        //if this filename is not default.aspx include it in the url for folder navigation
        if (!RequestPath.ToLower().EndsWith("/default.aspx"))
        {
            RootFolderNav = RequestPath;
        }
        else
        {
            RootFolderNav = RootFolder;
        }
        //what is the current folder
        CurrentFolder = "/";
        //is there a querystirng
        if (Request.QueryString["folder"] != null && Request.QueryString["folder"].ToString().Length > 0)
        {
            QueryString = Request.QueryString["folder"].TrimStart('/').TrimEnd('/').Trim();
            CurrentFolder = CurrentFolder + QueryString;
            DownloadLinkPrefix = RootFolder + CurrentFolder + "/";
            IsSubFolder = true;
        }
        else
        {
            DownloadLinkPrefix = RootFolder + "/";
        }
        //if the folder does not exist or is it blocked, then redirect to base url
        if (!Directory.Exists(Server.MapPath(RootFolder + CurrentFolder)) || BlockedFolders.Any(x => x.ToLower() == QueryString.ToLower()))
        {
            Response.Redirect(RootFolderNav);
        }
        //get the current folder info
        var di = new DirectoryInfo(Server.MapPath(RootFolder + CurrentFolder));
        //find all the files
        AllFiles = di.GetFiles("*", SearchOption.TopDirectoryOnly)
            .Where(x => AllowedFileTypes.Any(y => y.ToLower() == Path.GetExtension(x.Name).ToLower()))
            .OrderBy(y => y.Name).ToList();
        //find all the folders
        if (BrowseSubFolders)
        {
            AllFolders = di.GetDirectories()
                .Where(x => !BlockedFolders.Any(y => y.ToLower() == x.Name.ToLower()))
                .OrderBy(y => y.Name).ToList();
        }
        //which size to display
        SizeDivider = 1024;
        if (ShowSizeInMb)
        {
            SizeDivider = SizeDivider * SizeDivider;
        }
        //create the parent folder link
        if (IsSubFolder)
        {
            string LastPart = CurrentFolder.Split('/').Last();
            if (!string.IsNullOrEmpty(LastPart))
            {
                ParentFolder = CurrentFolder.Replace(LastPart, "").TrimEnd('/');
            }
        }
    }
</script>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <title>VDWWD FileBrowser - <%= CurrentFolder %></title>
</head>
<body>
    <!-- van der Waal Webdesign -->
    <!-- https://www.vanderwaal.eu -->
    <div class="container mt-3 mb-3">
        <!-- breadcrumb path -->
        <% if ((UseDataTables && UseDataTablesSearch) || (BrowseSubFolders && ShowBreadCrumbs))
            { %>
        <div class="row mb-3">
            <div class="col-12 col-md-9 col-xxl-10 h5">
                <% if (BrowseSubFolders && ShowBreadCrumbs)
                    { %>
                <div class="float-start fw-bold pe-2"><%= TxtCurrentFolder %>:</div>
                <nav class="float-start" aria-label="breadcrumb">
                    <ol class="breadcrumb ">
                        <% if (IsSubFolder)
                            {
                        %>
                        <li class="breadcrumb-item"><a class="text-decoration-none" href="<%= RootFolderNav %>"><%= TxtRoot %></a></li>
                        <%
                            var folders = CurrentFolder.Split('/').Skip(1).ToList();
                            for (int i = 0; i < folders.Count(); i++)
                            {
                                if (CurrentFolder.EndsWith("/" + folders[i]))
                                {
                        %>
                        <li class="breadcrumb-item active"><%= folders[i] %></li>
                        <% }
                            else
                            {  %>
                        <li class="breadcrumb-item"><a class="text-decoration-none" href="<%= RootFolderNav %>?folder=<%= "/" + string.Join("/", folders.Take(i + 1)) %>"><%= folders[i] %></a></li>
                        <% }
                                }
                            }
                            else
                            {  %>
                        <li class="breadcrumb-item"><%= CurrentFolder %></li>
                        <% } %>
                    </ol>
                </nav>
                <% } %>
            </div>
            <%
                if (UseDataTables && UseDataTablesSearch)
                {
            %>
            <div class="col-12 col-md-3 col-xl-2">
                <input type="search" placeholder="<%= TxtDatatableSearch %>" maxlength="50" class="form-control form-control-sm search-datatable">
            </div>
            <% } %>
        </div>
        <% } %>
        <div class="row">
            <div class="col">
                <!-- the table with all the files -->
                <table class="table table-striped table-datatable <%= UseDataTables ? "opacity-0" : "" %>" id="vdwwd_datatable_<%= Regex.Replace(CurrentFolder, "[^a-zA-Z0-9]", "") %>">
                    <thead>
                        <tr>
                            <% if (UseDataTables)
                                { %>
                            <th class="d-none"></th>
                            <% } %>
                            <th class="w-75"><%= TxtColumnFileName %> </th>
                            <th class="text-end"><%= TxtColumnDate %> </th>
                            <th class="text-end"><%= TxtColumnType %> </th>
                            <th class="text-end"><%= TxtColumnSize %> </th>
                        </tr>
                    </thead>
                    <tbody>
                        <!-- loop all the folders -->
                        <% if (BrowseSubFolders)
                            {
                                if (!string.IsNullOrEmpty(CurrentFolder))
                                {
                        %>
                        <!-- add the up folder link if not root -->
                        <% if (IsSubFolder)
                            { %>
                        <tr>
                            <% if (UseDataTables)
                                { %>
                            <td class="d-none">0</td>
                            <% } %>
                            <td>
                                <div class="float-start">
                                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="me-3">
                                        <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
                                        <line x1="9" y1="14" x2="15" y2="14"></line>
                                    </svg>
                                </div>
                                <div class="fw-bold">
                                    <a href="<%= RootFolderNav %>?folder=<%= ParentFolder %>" class="text-decoration-none">
                                        <%= TxtFolderUp %>
                                    </a>
                                </div>
                            </td>
                            <td></td>
                            <td></td>
                            <td></td>
                        </tr>
                        <% } %>
                        <!-- loop the folders -->
                        <%
                            }
                            foreach (var Folder in AllFolders)
                            { %>
                        <tr>
                            <% if (UseDataTables)
                                { %>
                            <td class="d-none">1</td>
                            <% } %>
                            <td>
                                <div class="float-start">
                                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="me-3">
                                        <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
                                    </svg>
                                </div>
                                <div class="fw-bold">
                                    <a href="<%= RootFolderNav %>?folder=<%= (IsSubFolder ? CurrentFolder + "/" : CurrentFolder) + Folder.Name.Replace(" ", "%20") %>" class="text-decoration-none">
                                        <%= Folder.Name %>
                                    </a>
                                </div>
                            </td>
                            <td></td>
                            <td></td>
                            <td></td>
                        </tr>
                        <% }
                            } %>
                        <!-- loop all the files -->
                        <% foreach (var File in AllFiles)
                            { %>
                        <tr>
                            <% if (UseDataTables)
                                { %>
                            <td class="d-none">2</td>
                            <% } %>
                            <td>
                                <a target="_blank" class="text-decoration-none" href="<%: DownloadLinkPrefix + File.Name %>">
                                    <div>
                                        <%= File.Name %>
                                    </div>
                                </a>
                            </td>
                            <td class="text-end text-nowrap">
                                <%= File.CreationTime.ToShortDateString() %>
                            </td>
                            <td class="text-end text-nowrap">
                                <%= File.Extension.Replace(".", "").ToUpper() %>
                            </td>
                            <td class="text-end text-nowrap">
                                <span class="d-none"><%: File.Length.ToString().PadLeft(15, '0') %></span>
                                <%= string.Format("{0:N1}", (decimal)File.Length / SizeDivider) %>
                            </td>
                        </tr>
                        <% } %>
                        <!-- if there are no files show error message -->
                        <% if (!AllFiles.Any())
                            { %>
                        <tr>
                            <% if (UseDataTables)
                                { %>
                            <td class="d-none">1</td>
                            <% } %>
                            <td class="dataTables_empty">
                                <%= TxtDatatableEmpty %>
                            </td>
                            <td></td>
                            <td></td>
                            <td></td>
                        </tr>
                        <% } %>
                    </tbody>
                    <!-- show the footer -->
                    <% if (ShowFooter)
                        { %>
                    <tfoot>
                        <tr>
                            <td colspan="5" class="fw-bold">
                                <%= string.Format(TxtFooter, AllFiles.Count(), (decimal)AllFiles.Sum(x => x.Length) / SizeDivider, ShowSizeInMb ? TxtSizeMb : TxtSizeKb) %>
                            </td>
                        </tr>
                    </tfoot>
                    <% } %>
                </table>
            </div>
        </div>
    </div>
    <div class="container">
        
        <div class="row">
            <div class="col text-center pt-4">
                <a target="_blank" href="https://www.vanderwaal.eu">
                    <img src="https://www.vanderwaal.eu/images/vdwwd.png" alt="van der Waal Webdesign" title="van der Waal Webdesign" width="20%" />
                </a>
            </div>
        </div>
    </div>
    <% if (UseDataTables)
        { %>
    <!-- if datatables are allowed -->
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
    <link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet" />
    <% if (UseDataTablesSearch)
        { %>
    <script src="https://cdn.jsdelivr.net/g/mark.js(jquery.mark.min.js)"></script>
    <script src="https://cdn.datatables.net/plug-ins/1.10.13/features/mark.js/datatables.mark.js"></script>
    <% } %>
    <style>
        .dataTables_filter {
            display: none;
        }
        table.dataTable td.dataTables_empty {
            text-align: left;
            padding: 20px;
            color: red;
        }
        mark {
            background: orange;
            color: black;
            padding: 0px 2px;
        }
    </style>
    <script>
        $(document).ready(function () {
            var $table = $('.table-datatable');
            var $search = $table.closest('.container').find('.search-datatable');
            //initialze the datatable
            var $datatable = $table.DataTable({
                'stateSave': true,
                'stateDuration': -1,
                'searching': <%= UseDataTablesSearch.ToString().ToLower() %>,
                'paging': false,
                'info': false,
                'mark': {
                    separateWordSearch: false
                },
                'orderFixed': [0, 'asc'],
                'language': {
                    'emptyTable': '<%= TxtDatatableEmpty %>',
                    'search': '<%= TxtDatatableSearch %>:',
                    'zeroRecords': '<%= TxtDatatableNohits %>'
                },
            });
            //only show the datatable after initialization to hide the flickering of content due to creating or sorting contents
            $table.removeClass('opacity-0');
    <% if (UseDataTablesSearch)
        { %>
            //search the datatable on keyup from an external input
            $search.on('keyup', function () {
                $datatable.search(this.value).draw();
            });
            //make the x button in the search input remove the search from the datatable
            $search.on('search', function () {
                if ($(this).val() === '') {
                    $search.trigger('keyup');
                }
            });
            //if there is a search saved in the savestate of the datatable then fill the external input with that value
            $search.val($datatable.search());
            <% } %>
        });
    </script>
    <% } %>
</body>
</html>