我目前正在尝试编写一个PowerShell函数,该函数与Lync powershell cmdlet" Export-CsConfiguration -AsBytes"的输出一起使用。使用Lync Cmdlet的隐式Powershell远程处理时,-AsBytes标志是处理Export-CsConfiguration cmdlet的唯一方法,它返回一个Byte数组,如果您使用" Set-Content - 将其写入磁盘 - 编码字节",产生一个zip文件。
我想知道是否有办法将字节数组的内容扩展为该zip文件中包含的两个文件,但只能在内存中进行。我并不真的对保持zip文件很长时间感兴趣,因为它经常更改,以及将文件内容写入磁盘只是为了再次直接读取它们,所以我可以用未压缩的内容做一些事情似乎对我来说可怕的错误。
所以有一些做这样的事情可以避免写入磁盘:
$ZipFileBytes = Export-CsConfiguration -AsBytes
# Made up Powershell function follows:
[xml]DocItemSet = Extract-FileFromInMemoryZip -ByteArray $ZipFileBytes -FileInsideZipIWant "DocItemSet.xml"
# Do stuff with XML here
而不是:
$ZipFileBytes = Export-CsConfiguration -AsBytes | Set-Content -Encoding Byte "CsConfig.zip"
[System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem')
[System.IO.Compression.ZipFile]::ExtractToDirectory("CsConfig.zip", "C:\Temp")
[xml]$DocItemSet = New-Object Xml.XmlDocument
$DocItemSet.Load("C:\Temp\DocItemSet.xml")
# Do stuff with XML here
或者我是SOL?
答案 0 :(得分:7)
在这里回答我自己的问题,以防它对别人有用:( N.B.需要.NET 4.5)
看起来将System.IO.Compression.ZipArchive与System.IO.Memorystream结合使用是前进的方向。我现在有了这个:
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
}
这正是我所需要的。
全部谢谢:)
答案 1 :(得分:0)
DotNetZip是你的朋友。 This SO post从流中读取,但它是C#代码。
以下代码是PowerShell,但尚未经过测试。但是,它应该是一个很好的起点。您应该在DotNetZip对象上获得intellisense,并且在他们的网站上有大量的帮助和示例代码。
#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()
答案 2 :(得分:0)
使“组成Powershell功能”成为现实:
#
# .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 }
)
}
}