Prof. Powershell
PowerShell File Frontier, Part 6: Adding Files to a Zip Archive
Prof. Powershell's rundown of what can be done with zipped files in PowerShell continues.
- By Jeffery Hicks
- 09/29/2014
More on this topic:
Last time we explored opening a zip file with PowerShell and the .NET Framework and seeing what was inside. Today, let's add some files to it. I'll go ahead and open my demo zip file but this time not in read-only mode. I know from last time that there is an Open() method but I don't know how to use it. So I'll look at the OverloadDefinitions again.
PS C:\> [System.IO.Compression.ZipFile]::open.OverloadDefinitions
static System.IO.Compression.ZipArchive Open(string archiveFileName, System.IO.Compression.ZipArchiveMode mode)
static System.IO.Compression.ZipArchive Open(string archiveFileName, System.IO.Compression.ZipArchiveMode mode, System.Text.Encoding entryNameEncoding)
There are two ways I can invoke the Open() method. Looks like both require the name of the zip archive. Both also seem to require a parameter indicating the ZipArchiveMode. The second parameter set can also take a parameter indicating encoding. The first method should be sufficient for our purposes. But how do we know what to use for a mode?
One technique is to put in a bogus value and hope that the error message indicates the proper values.
PS C:\> $zip = [System.IO.Compression.ZipFile]::Open("C:\zip\demo.zip","foo")
The result can be seen in Figure 1.
The error message indicates the correct values. Or I could use PowerShell to get the values since I know they are part of something called [System.IO.Compression.ZipArchiveMode]. This class is known as an enumeration which is essentially a list of possible values. The easy way to list the values is to use the builtin [enum] class and invoke the GetNames() static method.
PS C:\> [enum]::GetNames([System.IO.Compression.ZipArchiveMode])
Read
Create
Update
Since I want to add a file it seems clear I need to use the Update mode.
PS C:\> $zip = [System.IO.Compression.ZipFile]::Open("C:\zip\demo.zip","Update")
PS C:\> $zip
Entries Mode
------- ----
{chi-hvr2-health-full.htm, chi-hvr2-health.htm, EventLo... Update
Excellent. Ready to go. Oh, but how do I add a file? Back to Get-Member.
PS C:\> $zip | Get-Member -MemberType Method
From what you see in Figure 2 you might guess that you need to use the CreateEntry() method.
But this is when you need to turn to the documentation. When you read the MSDN docs on this class you'll learn that this method creates an empty entry that you can fill later. That's not what we want. Instead we need to take advantage of the other compression class [System.IO.Compress.ZipExtensions].
PS C:\> [System.IO.Compression.ZipFileExtensions].getmethods().Name | select -unique
CreateEntryFromFile
ExtractToDirectory
ExtractToFile
ToString
Equals
GetHashCode
GetType
This looks promising. Let's look closer the CreateEntryFromFile() method.
PS C:\> [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile.OverloadDefinitions
static System.IO.Compression.ZipArchiveEntry CreateEntryFromFile(System.IO.Compression.ZipArchive destination, string
sourceFileName, string entryName)
static System.IO.Compression.ZipArchiveEntry CreateEntryFromFile(System.IO.Compression.ZipArchive destination, string sourceFileName, string entryName, System.IO.Compression.CompressionLevel compressionLevel)
Looks like I need to specify a zip file and the source filename. It also looks like I can optionally specify a compression level. Bet that is another enumeration.
PS C:\> [enum]::GetNames([System.IO.Compression.CompressionLevel])
Optimal
Fastest
NoCompression
I know what I want to try.
PS C:\> [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip,
"C:\work\csdata.xml","csdata.xml","optimal")
Archive : System.IO.Compression.ZipArchive
CompressedLength :
FullName : csdata.xml
LastWriteTime : 12/5/2013 5:31:48 PM -05:00
Length :
Name : csdata.xml
The entry name is how I want the file to look in the zip file. You'll notice that lengths are empty. But don't worry. Close the file, re-open and look at the entries.
PS C:\> $zip.Dispose()
PS C:\> $zip = [System.IO.Compression.ZipFile]::Open("C:\zip\demo.zip","Update")
PS C:\> $zip.entries.where({$_.name -eq 'csdata.xml'})
Archive : System.IO.Compression.ZipArchive
CompressedLength : 313
FullName : csdata.xml
LastWriteTime : 12/5/2013 5:31:48 PM -05:00
Length : 1930
Name : csdata.xml
That's all the time we have for now. Next time we'll look at some other techniques and scenarios using these zip files in PowerShell.
About the Author
Jeffery Hicks is an IT veteran with over 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award in Windows PowerShell. He works today as an independent author, trainer and consultant. Jeff has written for numerous online sites and print publications, is a contributing editor at Petri.com, and a frequent speaker at technology conferences and user groups.