One of the more frustrating things in this day of PowerShell v3 is not having cmdlets that can simply manipulate ZIP archives.
It is right there in the Windows GUI, but is it easy to automate? Nope.
In fact if you search around you will find lots of different ways to handle this, one getting more complex than the next. You will also find community projects that attempt to do the same thing.
One common reference that I ran across was this:
From the spectacular David Aiken. I have to admit, it is not the first time he has saved my bacon.
His solution is built in, no funky community add-ins, nothing strange I can’t follow, and best of all it is compact – it is really small.
I have already had to do some things with Shell.Application with PowerShell, so I figured I would give it a shot.
Well, I immediately ran into some issues.
One, his use of –Recurse. Not necessary. Use Get-ChildItem and pipe in the folder and then entire folder is zipped. Perfect. So you can work the input just about any way you like and it will simply pass whatever you pipe to it.
Two, file locking. This tripped my up for hours. And lots of other folks that have found his solution too. His little 500 millisecond wait before advancing on to the next is simply not real nor flexible based on varying file sizes.
I found all kinds of folks commenting on the same thing and developing all kinds of fancy solutions to handle it. But, in the end I found something really simple, and it was buried right there in the shell.application all along. Simply test for the existence of the item you are zipping in the zip archive.
So simple. One little do loop. With my crafty Until ( $zipPackage.Items() | select {$_.Name -eq $file.Name} )
And, I only modified the Add-Zip, since David already had a fail safe to create the .ZIP if it didn’t already exist.
I am going to update my use of his usage example as well.
In my case I have a number of XML files. Each is in a unique Folder. There are other files and folders with the XML files as well (this is an OVF, for you OVF fans).
I want to create the .ZIP one level up from the folder where the XML is.
$zipFullName = $ovfFolder.Parent.FullName + "\" + $xmlFile.BaseName + ".zip" Get-ChildItem $ovfFolder | Add-Zip $zipFullName $ovfFolder = Get-Item $xmlFile.PSParentPath
Now, below is my modification to the original Add-Zip function.
if(!(test-path($zipfilename)))function Add-Zip # usage: Get-ChildItem $folder | Add-Zip $zipFullName
{
param([string]$zipfilename)
{
set-content $zipfilename ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
(dir $zipfilename).IsReadOnly = $false
}
$shellApplication = new-object -com shell.application
$zipPackage = $shellApplication.NameSpace($zipfilename)
foreach($file in $input)
{
$zipPackage.CopyHere($file.FullName)
do {
Start-sleep 2
} until ( $zipPackage.Items() | select {$_.Name -eq $file.Name} )
}
}