Skip to content

Instantly share code, notes, and snippets.

@jschlackman
Last active October 19, 2023 02:36
Show Gist options
  • Save jschlackman/05957a2a769ed17846e8e4c0a0feb23c to your computer and use it in GitHub Desktop.
Save jschlackman/05957a2a769ed17846e8e4c0a0feb23c to your computer and use it in GitHub Desktop.
# Name: Get-HtmlFolderReport.ps1
# Author: James Schlackman
# Last Modified: October 18 2023
#
# Outputs an HTML listing of all files in a specified folder, grouped in order by subfolder
Param(
[Parameter(Mandatory)] [String] $SearchPath,
[Parameter()] [String] $OutputPath = "$((Get-Date).ToString("yyMMdd")) $((Get-Item -Path $SearchPath).Name).html"
)
# Format file sizes with sensible units
Function Format-FileSize() {
Param ([int64]$size)
If ($size -gt 1TB) {[string]::Format("{0:0.00} TB", $size / 1TB)}
ElseIf ($size -gt 1GB) {[string]::Format("{0:0.00} GB", $size / 1GB)}
ElseIf ($size -gt 1MB) {[string]::Format("{0:0.0} MB", $size / 1MB)}
ElseIf ($size -gt 1KB) {[string]::Format("{0:0} KB", $size / 1KB)}
Else {[string]::Format("{0:0} B", $size)}
}
$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
$typeDesc = (Get-ItemProperty "Registry::HKEY_CLASSES_ROOT\$((Get-ItemProperty "Registry::HKEY_Classes_root\$($extension)")."(default)")")."(default)"
If ([bool]$typeDesc)
{
$fileTypeCache.Add($extension, $typeDesc)
} Else {
# Generate a default description if there is no file type association
$fileTypeCache.Add($extension, "$extension File".Trim())
}
}
# 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 $SearchPath) {
$folderName = '(Root)'
} Else {
$folderName = $folderName.Substring($SearchPath.Length + 1)
}
# Return folder name heading
"<h2 id=""$($DirectoryPath.GetHashCode())"">📂 $folderName</h2>"
# 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: $($subFolders.Count)<ul class=""folders"">`n"
$subFolders | ForEach-Object {
"<li><a href=#$($_.FullName.GetHashCode())>$($_.Name)</a></li>"
}
"</ul></p>"
}
If ([bool]$subFiles) {
$fileProperties = `
@{Name='File Name';Expression={"📄 $($_.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: $($subFiles.Count)</br>Total size: $($(Format-FileSize ($subFiles | Measure-Object -Property Length -Sum).Sum))</p>"
# 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 {
# Search the entire specified folder
$searchResults = Get-ChildItem -Path $SearchPath -Recurse
# Get file listing for totals
$allFiles = $searchResults | Where-Object {$_.Attributes -notcontains 'Directory'}
# Get all subfolders
$allFolders = $searchResults | Where-Object {$_.Attributes -contains '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: "📁 "}
</style></head>
"@
$reportIntro = "<h1>Report: $((Get-Item -Path $SearchPath).Name)</h1><p><p><ul><li>Total files found: $($allFiles.Count.ToString("N0"))</li><li>Subfolders: $($allFolders.Count.ToString("N0"))</li><li>Total size: $(Format-FileSize ($allFiles | Measure-Object -Property Length -Sum).Sum)</li></ul></p>"
# Get the file listing for the root folder
$reportBody = Export-HtmlFileList -DirectoryPath $SearchPath
# Append the file listings for each subfolder
$allFolders | Sort-Object -Property FullName | ForEach-Object {
$reportBody += Export-HtmlFileList -DirectoryPath $_.FullName
}
# Output the report to disk
"$reportHead`n$reportIntro`n$reportBody" | Out-File -FilePath $OutputPath -Encoding UTF8
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment