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.

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.

[Click on image for larger view.]  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.

[Click on image for larger view.]  Figure 2.

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.

comments powered by Disqus
Most   Popular