23

I am creating a nightly database schema file and would like to put all the files created each night, one for each database, into a folder and compress that folder. I have a PowerShell script that creates the schema.Only creation script of the db's and then adds all the files to a new folder. The problem lies within the compression portion of this process.

Does anybody have any idea if this can be accomplished with the pre-installed Windows utility that handles folder compression?

It would be best to use that utility if possible rather than something like 7zip (I don't feel like installing 7zip on every customers' server and it may take IT years to do it if I ask them).

Subrata
  • 2,216
  • 3
  • 24
  • 38
TechDawg270
  • 725
  • 3
  • 9
  • 20
  • I think this explains your options: http://richardspowershellblog.wordpress.com/2007/06/02/powershell-and-compressed-files/ – David Jun 13 '12 at 19:32
  • 1
    Thank you very much! This was a clean and easy solution. Directions for anybody else that may stumble upon this thread. Download PowerShell community extensions from: http://pscx.codeplex.com/downloads Extract files then move the directory to: C:\Windows\System32\WindowsPowerShell\v1.0\Modules. Then import the module by running the command: Import-Module Pscx – TechDawg270 Jun 14 '12 at 13:40

6 Answers6

23

A native way with latest .NET 4.5 framework, but entirely feature-less:

Creation:

Add-Type -Assembly "System.IO.Compression.FileSystem" ;
[System.IO.Compression.ZipFile]::CreateFromDirectory("c:\your\directory\to\compress", "yourfile.zip") ;

Extraction:

Add-Type -Assembly "System.IO.Compression.FileSystem" ;
[System.IO.Compression.ZipFile]::ExtractToDirectory("yourfile.zip", "c:\your\destination") ;

As mentioned, totally feature-less, so don't expect an overwrite flag.

sonjz
  • 4,870
  • 3
  • 42
  • 60
20

Here's a couple of zip-related functions that don't rely on extensions: Compress Files with Windows PowerShell.

The main function that you'd likely be interested in is:

function Add-Zip
{
    param([string]$zipfilename)

    if(-not (test-path($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)
            Start-sleep -milliseconds 500
    }
}

Usage:

dir c:\demo\files\*.* -Recurse | Add-Zip c:\demo\myzip.zip

There is one caveat: the shell.application object's NameSpace() function fails to open up the zip file for writing if the path isn't absolute. So, if you passed a relative path to Add-Zip, it'll fail with a null error, so the path to the zip file must be absolute.

Or you could just add a $zipfilename = resolve-path $zipfilename at the beginning of the function.

voithos
  • 68,482
  • 12
  • 101
  • 116
  • This did work and I appreciate the timely response, but I think downloading and installing the PowerShell community extensions and having the availability of the Write-Zip commandlet is a cleaner/easier method of achieving the functionality. – TechDawg270 Jun 14 '12 at 13:38
  • @TechDawg270: Agreed - I mostly just posted this for the sake of completeness. – voithos Jun 18 '12 at 21:06
8

As of PowersShell 5 there is a Compress-Archive cmdlet that does the task out of the box.

Overly Excessive
  • 2,095
  • 16
  • 31
2

This compresses .\in contents to .\out.zip with System.IO.Packaging.ZipPackage following the example here

$zipArchive = $pwd.path + "\out.zip"
[System.Reflection.Assembly]::Load("WindowsBase,Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
$ZipPackage=[System.IO.Packaging.ZipPackage]::Open($zipArchive, [System.IO.FileMode]"OpenOrCreate", [System.IO.FileAccess]"ReadWrite")
$in = gci .\in | select -expand fullName
[array]$files = $in -replace "C:","" -replace "\\","/"
ForEach ($file In $files) {
   $partName=New-Object System.Uri($file, [System.UriKind]"Relative")
   $part=$ZipPackage.CreatePart($partName, "application/zip", [System.IO.Packaging.CompressionOption]"Maximum")
   $bytes=[System.IO.File]::ReadAllBytes($file)
   $stream=$part.GetStream()
   $stream.Write($bytes, 0, $bytes.Length)
   $stream.Close()
                                                    }
$ZipPackage.Close()
noam
  • 1,914
  • 2
  • 20
  • 26
2

Used voithos' answer to zip files up in powershell, just had one problem with the Add-Zip function, the Start-sleep -milliseconds 500 caused problems if the file couldn't be fully zipped up in that time -> the next one starting before it was complete caused errors and some files not to be zipped.

So after playing around for a bit, first trying to get a counter going to check the count of the $zipPackage.Items() and only continuing after the items count increased (which did not work as it would return 0 in some cases when it should not) I found that it will return 0 if the package is still zipping/copying the files up (I think, haha). Added a simple while loop with the start-sleep inside of it, waiting for the zipPackage.Items().count to be a non-zero value before continuing and this seems to solve the problem.

function Add-Zip
{
param([string]$zipfilename)

if(-not (test-path($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 -milliseconds 250
        }
        while ($zipPackage.Items().count -eq 0)
}
}
Verts
  • 21
  • 1
-3

Using PowerShell Version 3.0:

Copy-ToZip -File ".\blah" -ZipFile ".\blah.zip" -Force

Hope this helps.

Chad Levy
  • 10,032
  • 7
  • 41
  • 69
phani
  • 11
  • 1