问题
How do I stop exporting then importing files in and out of Powershell when working with .zip
files (Expand-Archive
)?
I am currently using a temporary folder to extract the .zip
file.
Is there a variable or something I missed that would work better than the solution below?
$filename = 'foobar'
$Zip_in_Bytes | Set-Content -Encoding Byte -Path "C:\temp\filename.zip"
Expand-Archive -Path "C:\temp\filename.zip" -DestinationPath "C:\temp\" -Force
[xml]$xml = Get-Content -Path "C:\temp\filename.xml"
Remove-Item "C:\temp\filename.zip"
Remove-Item "C:\temp\filename.xml"
Expand-Archive
only support paths parameters, not objects
Is there a better way to handle .zip
files?
回答1:
Using System.IO.Compression you can work with byte arrays and streams rather than temporary files, but it's a bit more work than Expand-Archive
.
EDIT: Added Get-ZipEntryContent
and Add-ZipEntry
sample calls, and tweaked parameters making $ZipFilePath
optional.
@( 'System.IO.Compression','System.IO.Compression.FileSystem') | % { [void][System.Reflection.Assembly]::LoadWithPartialName($_) }
function Get-ZipEntryContent(#returns the bytes of the first matching entry
[string] $ZipFilePath, #optional - specify a ZipStream or path
[IO.Stream] $ZipStream = (New-Object IO.FileStream($ZipFilePath, [IO.FileMode]::Open)),
[string] $EntryPath){
$ZipArchive = New-Object IO.Compression.ZipArchive($ZipStream, [IO.Compression.ZipArchiveMode]::Read)
$buf = New-Object byte[] (0) #return an empty byte array if not found
$ZipArchive.GetEntry($EntryPath) | ?{$_} | %{ #GetEntry returns first matching entry or null if there is no match
$buf = New-Object byte[] ($_.Length)
Write-Verbose " reading: $($_.Name)"
$_.Open().Read($buf,0,$buf.Length)
}
$ZipArchive.Dispose()
$ZipStream.Close()
$ZipStream.Dispose()
return $buf
}
function Add-ZipEntry(#Adds an entry to the $ZipStream. Sample call: Add-ZipEntry -ZipFilePath "$PSScriptRoot\temp.zip" -EntryPath Test.xml -Content ([text.encoding]::UTF8.GetBytes("Testing"))
[string] $ZipFilePath, #optional - specify a ZipStream or path
[IO.Stream] $ZipStream = (New-Object IO.FileStream($ZipFilePath, [IO.FileMode]::OpenOrCreate)),
[string] $EntryPath,
[byte[]] $Content,
[switch] $OverWrite, #if specified, will not create a second copy of an existing entry
[switch] $PassThru ){#return a copy of $ZipStream
$ZipArchive = New-Object IO.Compression.ZipArchive($ZipStream, [IO.Compression.ZipArchiveMode]::Update, $true)
$ExistingEntry = $ZipArchive.GetEntry($EntryPath) | ?{$_}
If($OverWrite -and $ExistingEntry){
Write-Verbose " deleting existing $($ExistingEntry.FullName)"
$ExistingEntry.Delete()
}
$Entry = $ZipArchive.CreateEntry($EntryPath)
$WriteStream = New-Object System.IO.StreamWriter($Entry.Open())
$WriteStream.Write($Content,0,$Content.Length)
$WriteStream.Flush()
$WriteStream.Dispose()
$ZipArchive.Dispose()
If($PassThru){
$OutStream = New-Object System.IO.MemoryStream
$ZipStream.Seek(0, 'Begin') | Out-Null
$ZipStream.CopyTo($OutStream)
}
$ZipStream.Close()
$ZipStream.Dispose()
If($PassThru){$OutStream}
}
Here's an example of how you would call Add-ZipEntry
and Get-ZipEntryContent
functions completely in memory:
$NewZipStream = Add-ZipEntry -ZipStream (New-Object IO.MemoryStream) -EntryPath Test.xml -Content ([text.encoding]::UTF8.GetBytes("<xml><test>1</test>")) -PassThru
$bytes = Get-ZipEntryContent -ZipStream $NewZipStream -EntryPath 'Test.xml'
[text.encoding]::UTF8.GetString($bytes)
回答2:
Although Expand-Archive
doesn't accept objects, you can provide it the string property of the objects. E.G. $File.FullName
will be a String
.
Get-ChildItem C:\temp\ -Filter "*.zip" |
ForEach-Object {
Expand-Archive -Path $_.FullName -DestinationPath "C:\Temp\Extracted\$($_.BaseName)\"
}
来源:https://stackoverflow.com/questions/59097330/expand-archive-without-importing-and-exporting-files