Private/Test-FileByteIntegrity.ps1
|
function Test-FileByteIntegrity { <# .SYNOPSIS Performs a partial byte comparison between two files (first + last 8 KB). .DESCRIPTION Compares file existence, size, and the first/last 8 KB of content. Returns a status string indicating the result. .PARAMETER SourcePath Full path to the source file. .PARAMETER DestinationPath Full path to the destination file. .OUTPUTS [string] One of: 'Match', 'MissingOnSource', 'MissingOnDestination', 'SizeMismatch', 'ByteMismatch'. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)] [string]$SourcePath, [Parameter(Mandatory)] [string]$DestinationPath ) if (-not [System.IO.File]::Exists($SourcePath)) { return 'MissingOnSource' } if (-not [System.IO.File]::Exists($DestinationPath)) { return 'MissingOnDestination' } $srcInfo = [System.IO.FileInfo]::new($SourcePath) $dstInfo = [System.IO.FileInfo]::new($DestinationPath) if ($srcInfo.Length -ne $dstInfo.Length) { return 'SizeMismatch' } $chunkSize = 8192 $fileLen = $srcInfo.Length if ($fileLen -eq 0) { return 'Match' } $srcStream = [System.IO.File]::OpenRead($SourcePath) $dstStream = [System.IO.File]::OpenRead($DestinationPath) try { # Compare first chunk $readLen = [math]::Min($chunkSize, $fileLen) $srcBuf = [byte[]]::new($readLen) $dstBuf = [byte[]]::new($readLen) $srcStream.Read($srcBuf, 0, $readLen) | Out-Null $dstStream.Read($dstBuf, 0, $readLen) | Out-Null if (-not [System.Linq.Enumerable]::SequenceEqual($srcBuf, $dstBuf)) { return 'ByteMismatch' } # Compare last chunk (if file is larger than one chunk) if ($fileLen -gt $chunkSize) { $tailStart = [math]::Max(0, $fileLen - $chunkSize) $tailLen = [int]($fileLen - $tailStart) $srcBuf2 = [byte[]]::new($tailLen) $dstBuf2 = [byte[]]::new($tailLen) $srcStream.Seek($tailStart, [System.IO.SeekOrigin]::Begin) | Out-Null $dstStream.Seek($tailStart, [System.IO.SeekOrigin]::Begin) | Out-Null $srcStream.Read($srcBuf2, 0, $tailLen) | Out-Null $dstStream.Read($dstBuf2, 0, $tailLen) | Out-Null if (-not [System.Linq.Enumerable]::SequenceEqual($srcBuf2, $dstBuf2)) { return 'ByteMismatch' } } return 'Match' } finally { $srcStream.Dispose() $dstStream.Dispose() } } |