I\'m currently trying to write a powershell function which works with the output of the Lync powershell cmdlet \"Export-CsConfiguration -AsBytes\". When using implicit Power
Answering my own question here, in case it proves useful to others: (N.B. Requires .NET 4.5)
It looks like using System.IO.Compression.ZipArchive in combination with System.IO.Memorystream is the way forward. I've got this now:
Function Load-CsConfig{
[System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression') | Out-Null
$ZipBytes = Export-CsConfiguration -AsBytes
$ZipStream = New-Object System.IO.Memorystream
$ZipStream.Write($ZipBytes,0,$ZipBytes.Length)
$ZipArchive = New-Object System.IO.Compression.ZipArchive($ZipStream)
$ZipEntry = $ZipArchive.GetEntry('DocItemSet.xml')
$EntryReader = New-Object System.IO.StreamReader($ZipEntry.Open())
$DocItemSet = $EntryReader.ReadToEnd()
return $DocItemSet
}
Which does exactly what I need.
Thanks all :)
Making the "Made up Powershell function" a reality:
#
# .SYNOPSIS
# Extract a file from a byte[] zip file
#
# .DESCRIPTION
# Extracts a file from a byte[] zip file as byte[]
#
# .PARAMETER ByteArray
# Byte array containing zip file
#
# .PARAMETER FileInsideZipIWant
# The file inside zip I want
#
# .PARAMETER utf8
# If the file is UTF-8 encoded, use this switch to get a string
#
# .EXAMPLE
# PS C:\> $utf8str = Extract-FileFromInMemoryZip -ByteArray $ZipFileBytes -FileInsideZipIWant "DocItemSet.xml" -utf8
# PS C:\> $utf8str = Extract-FileFromInMemoryZip $ZipFileBytes "DocItemSet.xml" -utf8
# PS C:\> $bs = Extract-FileFromInMemoryZip $ZipFileBytes "DocItemSet.xml"
#
# .OUTPUTS
# string, byte[]
#
# .NOTES
# Exactly as desired. You may want to change the name of the "FileInsideZipIWant" parameter.
# Don't plan on extracting files larger than 2GB.
#
function Extract-FileFromInMemoryZip
{
[CmdletBinding(DefaultParameterSetName = 'raw')]
[OutputType([string], ParameterSetName = 'utf8')]
[OutputType([byte[]], ParameterSetName = 'raw')]
param
(
[Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0,
HelpMessage = 'Byte array containing zip file')]
[byte[]]$ByteArray,
[Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 1,
HelpMessage = 'Single file to extract')]
[string]$FileInsideZipIWant,
[Parameter(ParameterSetName = 'utf8')]
[switch]$utf8
)
BEGIN { Add-Type -AN System.IO.Compression -ea:Stop } # Stop on error
PROCESS {
$entry = (
New-Object System.IO.Compression.ZipArchive(
New-Object System.IO.MemoryStream ( ,$ByteArray)
)
).GetEntry($FileInsideZipIWant)
# Note ZipArchiveEntry.Length returns a long (rather than int),
# but we can't conveniently construct arrays longer than System.Int32.MaxValue
$b = [byte[]]::new($entry.Length)
# Avoid StreamReader to (dramatically) improve performance
# ...but it may be useful if the extracted file has a BOM header
$entry.Open().Read($b, 0, $b.Length)
write $(
if ($utf8) { [System.Text.Encoding]::UTF8.GetString($b) }
else { $b }
)
}
}
DotNetZip is your friend. This SO post reads from a stream, but it's C# code.
The code below is PowerShell, but is untested. However, it should hopefully be a good starting point for you. You should get intellisense on the DotNetZip objects and there is tons of help and example code on their website.
#load the DotNetZip assembly from disk and create a new zip object
[System.Reflection.Assembly]::LoadFrom("C:\Path\To\Ionic.Zip.dll")
$zipfile = New-Object Ionic.Zip.ZipFile
#your stream
$ZipFileBytes = Export-CsConfiguration -AsBytes
#your filename
$filename = "DocItemSet.xml"
$zipfile.Read($ZipFileBytes)
$file = $zipfile[$filename]
$stream = $file.OpenReader()
$buffer = New-Object Byte[] 4096
[int]$n
[string]$xmlFile = ""
cls
do {
$n = $stream.Read($buffer,0, $buffer.Length);
$totalBytesRead+=$n;
$xmlFile += [System.Text.Encoding]::ASCII.GetString($buffer)
} while ($n -gt 0);
$stream.Close()
$stream.Dispose()
$zipfile.Dispose()