PSCompression.psm1
using namespace System.IO using namespace System.IO.Compression using namespace System.Text using namespace System.Management.Automation using namespace System.Collections.Generic #Region '.\private\functions.ps1' 0 #using namespace System.IO #using namespace System.IO.Compression #using namespace System.Text function GzipFrameworkReader { [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] [FileInfo] $File, [Parameter()] [FileStream] $OutStream, [Parameter()] [switch] $Raw, [Parameter(Mandatory)] [Encoding] $Encoding ) process { try { # Credits to jborean - https://github.com/jborean93 for this craziness $stream = $File.OpenRead() $marker = 0 $outmem = [MemoryStream]::new() while (($b = $stream.ReadByte()) -ne -1) { if ($marker -eq 0 -and $b -eq 0x1F) { $marker += 1 } elseif ($marker -eq 1) { if ($b -eq 0x8B) { $marker += 1 } else { $marker = 0 } } elseif ($marker -eq 2) { $marker = 0 if ($b -eq 0x08) { try { $subStream = $File.OpenRead() $null = $subStream.Seek($stream.Position - 3, [SeekOrigin]::Begin) $gzip = [GZipStream]::new($subStream, [CompressionMode]::Decompress) if($PSBoundParameters.ContainsKey('OutStream')) { $gzip.CopyTo($OutStream) continue } $gzip.CopyTo($outmem) } finally { if($gzip -is [System.IDisposable]) { $gzip.Dispose() } if($subStream -is [System.IDisposable]) { $subStream.Dispose() } } } } } $null = $outmem.Seek(0, [SeekOrigin]::Begin) $reader = [StreamReader]::new($outmem, $Encoding) if($Raw.IsPresent) { return $reader.ReadToEnd() } while(-not $reader.EndOfStream) { $reader.ReadLine() } } finally { if($stream -is [IDisposable]) { $stream.Dispose() } if($reader -is [IDisposable]) { $reader.Dispose() } if($outmem -is [IDisposable]) { $outmem.Dispose() } } } } function GzipCoreReader { [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] [FileInfo] $File, [Parameter()] [FileStream] $OutStream, [Parameter()] [switch] $Raw, [Parameter(Mandatory)] [Encoding] $Encoding ) process { try { $inStream = $File.OpenRead() $gzip = [GZipStream]::new($inStream, [CompressionMode]::Decompress) if($PSBoundParameters.ContainsKey('OutStream')) { return $gzip.CopyTo($OutStream) } $reader = [StreamReader]::new($gzip, $Encoding, $true) if($Raw.IsPresent) { return $reader.ReadToEnd() } while(-not $reader.EndOfStream) { $reader.ReadLine() } } catch { $PSCmdlet.WriteError($_) } finally { if($gzip -is [System.IDisposable]) { $gzip.Dispose() } if($reader -is [System.IDisposable]) { $reader.Dispose() } if($inStream -is [System.IDisposable]) { $inStream.Dispose() } } } } #EndRegion '.\private\functions.ps1' 148 #Region '.\public\Compress-GZipArchive.ps1' 0 #using namespace System.IO #using namespace System.IO.Compression #using namespace System.Text #using namespace System.Management.Automation # .ExternalHelp PSCompression-help.xml function Compress-GzipArchive { [CmdletBinding(DefaultParameterSetName = 'Path')] [Alias('gziptofile')] [OutputType([System.IO.FileInfo])] param( [Parameter(ParameterSetName = 'PathWithUpdate', Mandatory, Position = 0, ValueFromPipeline)] [Parameter(ParameterSetName = 'PathWithForce', Mandatory, Position = 0, ValueFromPipeline)] [Parameter(ParameterSetName = 'Path', Mandatory, Position = 0, ValueFromPipeline)] [string[]] $Path, [Parameter(ParameterSetName = 'LiteralPathWithUpdate', Mandatory, ValueFromPipelineByPropertyName)] [Parameter(ParameterSetName = 'LiteralPathWithForce', Mandatory, ValueFromPipelineByPropertyName)] [Parameter(ParameterSetName = 'LiteralPath', Mandatory, ValueFromPipelineByPropertyName)] [Alias('PSPath')] [string[]] $LiteralPath, [Parameter(ParameterSetName = 'RawBytesWithUpdate', Mandatory, ValueFromPipeline)] [Parameter(ParameterSetName = 'RawBytesWithForce', Mandatory, ValueFromPipeline)] [Parameter(ParameterSetName = 'RawBytes', Mandatory, ValueFromPipeline)] [byte[]] $InputBytes, [Parameter(Position = 1, Mandatory)] [string] $DestinationPath, [Parameter()] [CompressionLevel] $CompressionLevel = 'Optimal', [Parameter(ParameterSetName = 'RawBytesWithUpdate', Mandatory)] [Parameter(ParameterSetName = 'PathWithUpdate', Mandatory)] [Parameter(ParameterSetName = 'LiteralPathWithUpdate', Mandatory)] [switch] $Update, [Parameter(ParameterSetName = 'RawBytesWithForce', Mandatory)] [Parameter(ParameterSetName = 'PathWithForce', Mandatory)] [Parameter(ParameterSetName = 'LiteralPathWithForce', Mandatory)] [switch] $Force, [Parameter()] [switch] $PassThru ) begin { if($Force.IsPresent) { $fsMode = [FileMode]::Create } elseif($Update.IsPresent) { $fsMode = [FileMode]::Append } else { $fsMode = [FileMode]::CreateNew } $expectingInput = $null } process { try { if($withPath = -not $PSBoundParameters.ContainsKey('InputBytes')) { $isLiteral = $PSBoundParameters.ContainsKey('LiteralPath') $paths = $Path if($isLiteral) { $paths = $LiteralPath } $items = $PSCmdlet.InvokeProvider.Item.Get($paths, $true, $isLiteral) } if(-not $expectingInput) { $expectingInput = $true $DestinationPath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($DestinationPath) if([Path]::GetExtension($DestinationPath) -ne '.gz') { $DestinationPath = $DestinationPath + '.gz' } $null = [Directory]::CreateDirectory([Path]::GetDirectoryName($DestinationPath)) $outStream = [File]::Open($DestinationPath, $fsMode) $gzip = [GZipStream]::new($outStream, [CompressionMode]::Compress, $CompressionLevel) if(-not $withPath) { $inStream = [MemoryStream]::new($InputBytes) } } foreach($item in $items) { try { $inStream = $item.OpenRead() $inStream.CopyTo($gzip) } catch { $PSCmdlet.WriteError($_) } finally { if($inStream -is [IDisposable]) { $inStream.Dispose() } } } } catch { if($gzip -is [System.IDisposable]) { $gzip.Dispose() } if($outStream -is [System.IDisposable]) { $outStream.Dispose() } if($inStream -is [System.IDisposable]) { $inStream.Dispose() } $PSCmdlet.ThrowTerminatingError($_) } } end { try { if(-not $withPath) { $inStream.Flush() $inStream.CopyTo($outStream) } } catch { $PSCmdlet.ThrowTerminatingError($_) } finally { if($gzip -is [System.IDisposable]) { $gzip.Dispose() } if($outStream -is [System.IDisposable]) { $outStream.Dispose() } if($inStream -is [System.IDisposable]) { $inStream.Dispose() } if($PassThru.IsPresent) { $outStream.Name -as [FileInfo] } } } } #EndRegion '.\public\Compress-GZipArchive.ps1' 151 #Region '.\public\Compress-ZipArchive.ps1' 0 #using namespace System.IO #using namespace System.IO.Compression #using namespace System.Collections.Generic # .ExternalHelp PSCompression-help.xml function Compress-ZipArchive { [CmdletBinding(DefaultParameterSetName = 'Path')] [Alias('zip', 'ziparchive')] [OutputType([System.IO.FileInfo])] param( [Parameter(ParameterSetName = 'PathWithUpdate', Mandatory, Position = 0, ValueFromPipeline)] [Parameter(ParameterSetName = 'PathWithForce', Mandatory, Position = 0, ValueFromPipeline)] [Parameter(ParameterSetName = 'Path', Mandatory, Position = 0, ValueFromPipeline)] [string[]] $Path, [Parameter(ParameterSetName = 'LiteralPathWithUpdate', Mandatory, ValueFromPipelineByPropertyName)] [Parameter(ParameterSetName = 'LiteralPathWithForce', Mandatory, ValueFromPipelineByPropertyName)] [Parameter(ParameterSetName = 'LiteralPath', Mandatory, ValueFromPipelineByPropertyName)] [Alias('PSPath')] [string[]] $LiteralPath, [Parameter(Position = 1, Mandatory)] [string] $DestinationPath, [Parameter()] [CompressionLevel] $CompressionLevel = [CompressionLevel]::Optimal, [Parameter(ParameterSetName = 'PathWithUpdate', Mandatory)] [Parameter(ParameterSetName = 'LiteralPathWithUpdate', Mandatory)] [switch] $Update, [Parameter(ParameterSetName = 'PathWithForce', Mandatory)] [Parameter(ParameterSetName = 'LiteralPathWithForce', Mandatory)] [switch] $Force, [Parameter()] [switch] $PassThru ) begin { $DestinationPath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($DestinationPath) if([Path]::GetExtension($DestinationPath) -ne '.zip') { $DestinationPath = $DestinationPath + '.zip' } $zipMode = [ZipArchiveMode]::Update if($Force.IsPresent) { $fsMode = [FileMode]::Create } elseif($Update.IsPresent) { $fsMode = [FileMode]::OpenOrCreate } else { $fsMode = [FileMode]::CreateNew $zipMode = [ZipArchiveMode]::Create } $ExpectingInput = $null } process { $isLiteral = $false $targetPath = $Path if($PSBoundParameters.ContainsKey('LiteralPath')) { $isLiteral = $true $targetPath = $LiteralPath } if(-not $ExpectingInput) { try { $null = [Directory]::CreateDirectory([Path]::GetDirectoryName($DestinationPath)) $destfs = [File]::Open($DestinationPath, $fsMode) $zip = [ZipArchive]::new($destfs, $zipMode) $ExpectingInput = $true } catch { $zip, $destfs | ForEach-Object Dispose $PSCmdlet.ThrowTerminatingError($_) } } $queue = [Queue[FileSystemInfo]]::new() foreach($item in $PSCmdlet.InvokeProvider.Item.Get($targetPath, $true, $isLiteral)) { $queue.Enqueue($item) $here = $item.Parent.FullName if($item -is [FileInfo]) { $here = $item.Directory.FullName } while($queue.Count) { try { $current = $queue.Dequeue() if($current -is [DirectoryInfo]) { $current = $current.EnumerateFileSystemInfos() } } catch { $PSCmdlet.WriteError($_) continue } foreach($item in $current) { try { if($item.FullName -eq $DestinationPath) { continue } $relative = $item.FullName.Substring($here.Length + 1) if($Update.IsPresent) { $entry = $zip.GetEntry($relative) } if($item -is [DirectoryInfo]) { $queue.Enqueue($item) if(-not $Update.IsPresent -or -not $entry) { $entry = $zip.CreateEntry($relative + '\', $CompressionLevel) } continue } if(-not $Update.IsPresent -or -not $entry) { $entry = $zip.CreateEntry($relative, $CompressionLevel) } $sourcefs = $item.Open([FileMode]::Open, [FileAccess]::Read, [FileShare] 'ReadWrite, Delete') $entryfs = $entry.Open() $sourcefs.CopyTo($entryfs) } catch { $PSCmdlet.WriteError($_) } finally { if($entryfs -is [System.IDisposable]) { $entryfs.Dispose() } if($sourcefs -is [System.IDisposable]) { $sourcefs.Dispose() } } } } } } end { if($zip -is [System.IDisposable]) { $zip.Dispose() } if($destfs -is [System.IDisposable]) { $destfs.Dispose() } if($PassThru.IsPresent) { $DestinationPath -as [FileInfo] } } } #EndRegion '.\public\Compress-ZipArchive.ps1' 163 #Region '.\public\ConvertFrom-GzipString.ps1' 0 #using namespace System.IO #using namespace System.IO.Compression #using namespace System.Text # .ExternalHelp PSCompression-help.xml function ConvertFrom-GzipString { [CmdletBinding()] [Alias('gzipfromstring')] [OutputType([string])] param( [Parameter(Mandatory, ValueFromPipeline)] [string[]] $InputObject, [Parameter()] [PSCompression.EncodingTransformation()] [ArgumentCompleter([PSCompression.EncodingCompleter])] [Encoding] $Encoding = [UTF8Encoding]::new(), [Parameter()] [switch] $Raw ) process { foreach($string in $InputObject) { try { $inStream = [MemoryStream]::new([Convert]::FromBase64String($string)) $gzip = [GZipStream]::new($inStream, [CompressionMode]::Decompress) $reader = [StreamReader]::new($gzip, $Encoding, $true) if($Raw.IsPresent) { return $reader.ReadToEnd() } while(-not $reader.EndOfStream) { $reader.ReadLine() } } catch { $PSCmdlet.WriteError($_) } finally { if($reader -is [System.IDisposable]) { $reader.Dispose() } if($gzip -is [System.IDisposable]) { $gzip.Dispose() } if($inStream -is [System.IDisposable]) { $inStream.Dispose() } } } } } #EndRegion '.\public\ConvertFrom-GzipString.ps1' 57 #Region '.\public\ConvertTo-GzipString.ps1' 0 #using namespace System.IO #using namespace System.IO.Compression #using namespace System.Text # .ExternalHelp PSCompression-help.xml function ConvertTo-GzipString { [CmdletBinding()] [Alias('gziptostring')] [OutputType([byte], ParameterSetName = 'ByteStream')] [OutputType([string])] param( [AllowEmptyString()] [Parameter(Mandatory, ValueFromPipeline)] [string[]] $InputObject, [Parameter()] [PSCompression.EncodingTransformation()] [ArgumentCompleter([PSCompression.EncodingCompleter])] [Encoding] $Encoding = [UTF8Encoding]::new(), [Parameter()] [CompressionLevel] $CompressionLevel = 'Optimal', [Parameter(ParameterSetName = 'ByteStream')] [Alias('Raw')] [switch] $AsByteStream, [Parameter()] [switch] $NoNewLine ) begin { $inStream = [MemoryStream]::new() $newLine = $Encoding.GetBytes([Environment]::NewLine) } process { foreach($string in $InputObject) { $bytes = $Encoding.GetBytes($string) $inStream.Write($bytes, 0, $bytes.Length) if($NoNewLine.IsPresent) { continue } $inStream.Write($newLine, 0, $newLine.Length) } } end { try { $outStream = [MemoryStream]::new() $gzip = [GZipStream]::new($outStream, [CompressionMode]::Compress, $CompressionLevel) $inStream.Flush() $inStream.WriteTo($gzip) } catch { $PSCmdlet.WriteError($_) } finally { if($gzip -is [System.IDisposable]) { $gzip.Dispose() } if($outStream -is [System.IDisposable]) { $outStream.Dispose() } if($inStream -is [System.IDisposable]) { $inStream.Dispose() } } try { if($AsByteStream.IsPresent) { return $PSCmdlet.WriteObject($outStream.ToArray()) } [Convert]::ToBase64String($outStream.ToArray()) } catch { $PSCmdlet.WriteError($_) } } } #EndRegion '.\public\ConvertTo-GzipString.ps1' 82 #Region '.\public\Expand-GzipArchive.ps1' 0 #using namespace System.IO #using namespace System.IO.Compression #using namespace System.Text # .ExternalHelp PSCompression-help.xml function Expand-GzipArchive { [CmdletBinding(PositionalBinding = $false)] [Alias('gzipfromfile')] [OutputType([string], ParameterSetName = ('Path', 'LiteralPath'))] [OutputType([System.IO.FileInfo], ParameterSetName = ('PathDestination', 'LiteralPathDestination'))] param( [Parameter(ParameterSetName = 'Path', Mandatory, Position = 0, ValueFromPipeline)] [Parameter(ParameterSetName = 'PathDestination', Mandatory, Position = 0, ValueFromPipeline)] [string[]] $Path, [Parameter(ParameterSetName = 'LiteralPath', Mandatory, ValueFromPipelineByPropertyName)] [Parameter(ParameterSetName = 'LiteralPathDestination', Mandatory, ValueFromPipelineByPropertyName)] [Alias('PSPath')] [string[]] $LiteralPath, [Parameter(Mandatory, ParameterSetName = 'PathDestination')] [Parameter(Mandatory, ParameterSetName = 'LiteralPathDestination')] [string] $DestinationPath, [Parameter(ParameterSetName = 'Path')] [Parameter(ParameterSetName = 'LiteralPath')] [PSCompression.EncodingTransformation()] [ArgumentCompleter([PSCompression.EncodingCompleter])] [Encoding] $Encoding = [UTF8Encoding]::new(), [Parameter(ParameterSetName = 'Path')] [Parameter(ParameterSetName = 'LiteralPath')] [switch] $Raw, [Parameter(ParameterSetName = 'PathDestination')] [Parameter(ParameterSetName = 'LiteralPathDestination')] [switch] $PassThru ) begin { $ExpectingInput = $null $params = @{ Raw = $Raw.IsPresent Encoding = $Encoding } } process { try { $isLiteral = $PSBoundParameters.ContainsKey('LiteralPath') $paths = $Path if($isLiteral) { $paths = $LiteralPath } $items = $PSCmdlet.InvokeProvider.Item.Get($paths, $true, $isLiteral) if(-not $ExpectingInput -and $PSBoundParameters.ContainsKey('DestinationPath')) { $ExpectingInput = $true $DestinationPath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($DestinationPath) $null = [Directory]::CreateDirectory([Path]::GetDirectoryName($DestinationPath)) $params['OutStream'] = [File]::Open($DestinationPath, [FileMode]::Append) } # Had to do this to read appended Gzip content in .NET Framework... if($IsCoreCLR) { return $items | GzipCoreReader @params } $items | GzipFrameworkReader @params } catch { $PSCmdlet.WriteError($_) } finally { if($params['OutStream'] -is [IDisposable]) { $params['OutStream'].Dispose() } if($PassThru.IsPresent) { $params['OutStream'].Name -as [FileInfo] } } } } #EndRegion '.\public\Expand-GzipArchive.ps1' 86 #Region 'PREFIX' 0 [System.IO.Path]::Combine($PSScriptRoot, 'bin', 'netstandard2.0', 'PSCompression.dll') | Import-Module #EndRegion 'PREFIX' |