Search code examples
powershellzlibsystem.io.compression

Zlib compression in Powershell


I need to compress a string to Zlib in PowerShell.

Is there a way we can compress using the ZLibStream class in PowerShell?

https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.zlibstream?view=net-8.0

I was able to compress using the class DeflateStream and GZipStream, but the external system which uses the data was not able to decode it.

This is the powershell script I tried with

$stringToCompress  = 'string to compress'

$memStream = New-Object System.IO.MemoryStream
$deflateStream = New-Object System.IO.Compression.GZipStream($memStream, [System.IO.Compression.CompressionMode]::Compress)
$streamWriter = New-Object System.IO.StreamWriter($deflateStream)
$streamWriter.Write($stringToCompress)
$streamWriter.Close();
$encodedCompressedString = [System.Convert]::ToBase64String($memStream.ToArray())
Write-Output $encodedCompressedString 

Solution

  • The process of compression and expansion with ZLibStream is pretty much the same as with GZipStream.

    Note that ZlibStream only works with .NET 6+ meaning that it is not available for Windows PowerShell 5.1 (.NET Framework 4.5+). If you want to use this class you will need to install PowerShell 7+.

    # Choose encoding here, need to use the same for compress & decompress
    $enc = [System.Text.Encoding]::UTF8
    
    # - Compress:
    $string = 'hello world!'
    
    $outstream = [System.IO.MemoryStream]::new()
    $zlib = [System.IO.Compression.ZLibStream]::new(
        $outstream,
        [System.IO.Compression.CompressionMode]::Compress)
    
    $bytes = $enc.GetBytes($string)
    $zlib.Write($bytes, 0, $bytes.Length)
    $zlib.Dispose()
    
    [System.Convert]::ToBase64String($outstream.ToArray())
    # eJzLSM3JyVcozy/KSVEEAB6JBH4=
    
    # - Decompress:
    $instream = [System.IO.MemoryStream]::new(
        [System.Convert]::FromBase64String('eJzLSM3JyVcozy/KSVEEAB6JBH4='))
    
    $zlib = [System.IO.Compression.ZLibStream]::new(
        $instream,
        [System.IO.Compression.CompressionMode]::Decompress)
    
    $outstream = [System.IO.MemoryStream]::new()
    $zlib.CopyTo($outstream)
    $zlib.Dispose()
    $enc.GetString($outstream.ToArray())
    # hello world!
    

    An alternative for Windows PowerShell 5.1 can be to use the ZLibWrapper package.

    First download the package and expand it:

    $invokeWebRequestSplat = @{
        OutFile = 'Joveler.ZLibWrapper.zip'
        Uri     = 'https://www.nuget.org/api/v2/package/Joveler.ZLibWrapper'
    }
    Invoke-WebRequest @invokeWebRequestSplat
    Expand-Archive 'Joveler.ZLibWrapper.zip'
    

    Then the flow is pretty similar using their assembly:

    Add-Type -Path .\Joveler.ZLibWrapper\lib\net45\Joveler.ZLibWrapper.dll
    
    # Choose encoding here, need to use the same for compress & decompress
    $enc = [System.Text.Encoding]::UTF8
    
    # - Compress:
    $string = 'hello world!'
    
    $outstream = [System.IO.MemoryStream]::new()
    $zlib = [Joveler.ZLibWrapper.ZlibStream]::new(
        $outstream,
        [Joveler.ZLibWrapper.ZLibMode]::Compress)
    
    $bytes = $enc.GetBytes($string)
    $zlib.Write($bytes, 0, $bytes.Length)
    $zlib.Dispose()
    
    [System.Convert]::ToBase64String($outstream.ToArray())
    
    # - Decompress:
    $instream = [System.IO.MemoryStream]::new(
        [System.Convert]::FromBase64String('eJzLSM3JyVcozy/KSVEEAB6JBH4='))
    
    $zlib = [Joveler.ZLibWrapper.ZLibStream]::new(
        $instream,
        [Joveler.ZLibWrapper.ZLibMode]::Decompress)
    
    $outstream = [System.IO.MemoryStream]::new()
    $zlib.CopyTo($outstream)
    $zlib.Dispose()
    $enc.GetString($outstream.ToArray())
    # hello world!