Last active
March 6, 2025 22:06
-
-
Save jschlackman/05957a2a769ed17846e8e4c0a0feb23c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| Outputs an HTML listing of all files in a specified folder, grouped in order by subfolder. | |
| .DESCRIPTION | |
| Enumerates all files and subfolders within a specified path and creates an HTML report listing name, file type, size, created date, and modified date. | |
| Author: James Schlackman <[email protected]> | |
| Last Modified: March 6 2025 | |
| .PARAMETER SearchPath | |
| Name of the path to enumerate. | |
| .PARAMETER OutputPath | |
| Pathname of the output file. Defaults to a file in the current directory with the name of the enumerated folder prefixed with the current date in yyMMdd format. | |
| #> | |
| Param( | |
| [Parameter(Mandatory)] [String] $SearchPath, | |
| [Parameter()] [String] $OutputPath = ('{0:yyMMdd} {1}.html' -f (Get-Date), (Get-Item -Path $SearchPath).Name) | |
| ) | |
| # Format file sizes with sensible units | |
| Function Format-FileSize() { | |
| Param ([int64]$size) | |
| If ($size -gt 1TB) {'{0:0.00} TB' -f ($size / 1TB)} | |
| ElseIf ($size -gt 1GB) {'{0:0.00} GB' -f ($size / 1GB)} | |
| ElseIf ($size -gt 1MB) {'{0:0.0} MB' -f ($size / 1MB)} | |
| ElseIf ($size -gt 1KB) {'{0:0} KB' -f ($size / 1KB)} | |
| Else {'{0:0} B' -f $size} | |
| } | |
| $emFolder = [Char]::ConvertFromUtf32(0x1F4C2) | |
| $emFile = [Char]::ConvertFromUtf32(0x1F4C4) | |
| $fileTypeCache = @{} | |
| # Get file type description from registry | |
| function Get-FileType() { | |
| param( | |
| [Parameter(Mandatory)] [string] $extension | |
| ) | |
| # Check if the file type description is already cached | |
| If (!($fileTypeCache.ContainsKey($extension))) { | |
| # If not, get it from the registry | |
| $typeClass = (Get-ItemProperty -Path ('Registry::HKEY_CLASSES_ROOT\{0}' -f $extension)).'(default)' | |
| $typeDesc = (Get-ItemProperty -Path ('Registry::HKEY_CLASSES_ROOT\{0}' -f $typeClass)).'(default)' | |
| If (!$typeDesc) | |
| { | |
| # Generate a default description if there is no file type association | |
| $typeDesc = '{0} File' -f $extension | |
| } | |
| # Add to the cache | |
| $fileTypeCache.Add($extension, $typeDesc) | |
| } | |
| # Return the description from the cache | |
| $fileTypeCache[$extension] | |
| } | |
| # Get HTML representation of file listing for a single folder | |
| function Export-HtmlFileList() { | |
| param( | |
| [Parameter(Mandatory)] [string] $DirectoryPath | |
| ) | |
| $folderName = $DirectoryPath | |
| If ($folderName -eq $rootFolder.FullName) { | |
| $folderName = '(Root)' | |
| } Else { | |
| $folderName = $folderName.Substring($rootFolder.FullName.Length + 1) | |
| } | |
| # Return folder name heading | |
| '<h2 id="{0}">{1} {2}</h2>' -f $DirectoryPath.GetHashCode(), $emFolder, $folderName | |
| # Get listings of files in this folder and dirext subfolders | |
| $subFolders = $allFolders | Where-Object {$_.Parent.FullName -eq $DirectoryPath} | |
| $subFiles = $allFiles | Where-Object {$_.DirectoryName -eq $DirectoryPath} | |
| # If the folder is not empty | |
| If ([bool]$subFolders -or [bool]$subFiles) { | |
| # Return a list of links to subfolders | |
| If ([bool]$subFolders) { | |
| '<p>Subfolders: {0}<ul class="folders">' -f $($subFolders).Count | |
| "`n" | |
| $subFolders | ForEach-Object { | |
| '<li><a href=#{0}>{1}</a></li>' -f $_.FullName.GetHashCode(), $_.Name | |
| "`n" | |
| } | |
| "</ul></p>`n" | |
| } | |
| If ([bool]$subFiles) { | |
| $fileProperties = ` | |
| @{Name='File Name';Expression={'{0} {1}' -f $emFile, $_.BaseName}}, | |
| @{Name='File Type';Expression={Get-FileType($_.Extension)}}, | |
| @{Name='Size';Expression={Format-FileSize $_.Length}}, | |
| @{Name='Created';Expression={Get-Date -UFormat '%D' $_.CreationTime}}, | |
| @{Name='Modified';Expression={Get-Date -UFormat '%D' $_.LastWriteTime}} | |
| # Return file listing summary | |
| '<p>Files: {0}</br>Total size: {1}</p>' -f @($subFiles).Count, (Format-FileSize (@($subFiles) | Measure-Object -Property Length -Sum).Sum) | |
| # Return file listing table | |
| $subFiles | Select $fileProperties | ConvertTo-Html -Fragment | |
| } | |
| } Else {'<p><em>Empty folder</em></p>'} | |
| } | |
| If (!(Test-Path $SearchPath)) { | |
| Write-Host "Folder not found: $SearchPath" -ForegroundColor Red | |
| } Else { | |
| $rootFolder = Get-Item -Path $SearchPath | |
| # Search the entire specified folder | |
| $searchResults = Get-ChildItem -Path $rootFolder.FullName -Recurse | |
| # Get file listing for totals | |
| $allFiles = $searchResults | Where-Object {!($_.Attributes -band [System.IO.FileAttributes]'Directory')} | |
| # Get all subfolders | |
| $allFolders = $searchResults | Where-Object {$_.Attributes -band [System.IO.FileAttributes]'Directory'} | |
| # Build report header and intro | |
| $reportHead = @" | |
| <head><style> | |
| body {font-family: Calibri,sans-serif; font-size: 11pt} | |
| h1 {font-family: Segoe UI Light,sans-serif} | |
| p.footer {margin-top: 3em; font-size: 9pt; font-style: italic; color: gray} | |
| table {background-color: #e6e6e6} | |
| td,th {background-color: white; padding: 3px} | |
| th {background-color: #f2f2f2} | |
| ul.folders {list-style: none; padding: 0; margin: 0;} | |
| ul.folders li {padding-left: 1rem; text-indent: -0.7rem} | |
| ul.folders li::before {content: "$($emFolder) "} | |
| </style></head> | |
| "@ | |
| $reportIntro = '<h1>Report: {0}</h1><p><p><ul><li>Total files found: {1}</li><li>Subfolders: {2}</li><li>Total size: {3}</li></ul></p>' -f | |
| $rootFolder.Name, | |
| $allFiles.Count.ToString('N0'), | |
| $allFolders.Count.ToString('N0'), | |
| (Format-FileSize ($allFiles | Measure-Object -Property Length -Sum).Sum) | |
| # Get the file listing for the root folder | |
| $reportBody = Export-HtmlFileList -DirectoryPath $rootFolder.FullName | |
| # Append the file listings for each subfolder | |
| $allFolders | Sort-Object -Property FullName | ForEach-Object { | |
| $reportBody += Export-HtmlFileList -DirectoryPath $_.FullName | |
| } | |
| # Output the report to disk | |
| "<html>$reportHead`n<body>$reportIntro`n$reportBody</body></html>" | Out-File -FilePath $OutputPath -Encoding UTF8 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment