4

I'm trying to zip all the files in a single directory to a different folder as part of a simple backup routine.

The code runs ok but doesn't produce a zip file:

$srcdir = "H:\Backup"
$filename = "test.zip"
$destpath = "K:\"

$zip_file = (new-object -com shell.application).namespace($destpath + "\"+ $filename)
$destination = (new-object -com shell.application).namespace($destpath)

$files = Get-ChildItem -Path $srcdir

foreach ($file in $files) 
{
    $file.FullName;
    if ($file.Attributes -cne "Directory")
    {
        $destination.CopyHere($file, 0x14);
    }
}

Any ideas where I'm going wrong?

8
  • 1
    Simply adding .zip to the end of a file doesn't make it a .zip archive. Are you using PowerShell 2.0 or 3.0? If you're using 3.0, it's a heck of a lot easier.
    – MDMarra
    Commented Dec 7, 2012 at 15:53
  • @MDMarra - Yes, powershell 3 (developed on Win7, deployed on Server 2003), isn't that what the "-com shell.application" does? Or have I messed that up?
    – SteB
    Commented Dec 7, 2012 at 15:58
  • See the bottom of this answer for how to do this in PS3
    – MDMarra
    Commented Dec 7, 2012 at 16:13
  • @MDMarra - I now get a "make sure that the assembly containing this type is loaded." error, I've tried "[System.AppDomain]::CurrentDomain.GetAssemblies()" but can't see a compression dll
    – SteB
    Commented Dec 7, 2012 at 16:27
  • Sorry, do you have .NET 4.5 installed? It's actually not a native PS3 thing, but rather a .NET 4.5 thing. My mistake for assuming you'd have both installed.
    – MDMarra
    Commented Dec 7, 2012 at 16:30

2 Answers 2

5

This works in V2, should work in V3 too:

$srcdir = "H:\Backup"
$zipFilename = "test.zip"
$zipFilepath = "K:\"
$zipFile = "$zipFilepath$zipFilename"

#Prepare zip file
if(-not (test-path($zipFile))) {
    set-content $zipFile ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
    (dir $zipFile).IsReadOnly = $false  
}

$shellApplication = new-object -com shell.application
$zipPackage = $shellApplication.NameSpace($zipFile)
$files = Get-ChildItem -Path $srcdir | where{! $_.PSIsContainer}

foreach($file in $files) { 
    $zipPackage.CopyHere($file.FullName)
#using this method, sometimes files can be 'skipped'
#this 'while' loop checks each file is added before moving to the next
    while($zipPackage.Items().Item($file.name) -eq $null){
        Start-sleep -seconds 1
    }
}
4
  • That looked good, but when I tried it nothing happened. I didn't get a zip file but I also didn't get any errors. Could you expand on what the Set-Content and ReadOnly = false lines do?
    – SteB
    Commented Dec 10, 2012 at 9:47
  • The set-content line adds the file header and creates a 0 byte file, ReadOnly = $false ensures we can copy files into the file.
    – nimizen
    Commented Dec 10, 2012 at 10:09
  • I've updated my answer - the $zipFilepath & $zipFilename weren't being joined correctly.
    – nimizen
    Commented Dec 10, 2012 at 10:38
  • Sorry, it was my fault, I'd accidentally changed it to Test-Path-Path! That works great now.
    – SteB
    Commented Dec 10, 2012 at 10:58
5

I discovered 2 additional ways to do this and am including them for reference:

Using the .Net framework 4.5 (as suggested by @MDMarra):

[Reflection.Assembly]::LoadWithPartialName( "System.IO.Compression.FileSystem" )
[System.AppDomain]::CurrentDomain.GetAssemblies()
$src_folder = "h:\backup"
$destfile = "k:\test.zip"
$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
$includebasedir = $false
[System.IO.Compression.ZipFile]::CreateFromDirectory($src_folder, $destfile, $compressionLevel, $includebasedir)

This worked great on my Win7 development machine and is probably the best way to do this, but .Net 4.5 is only supported on the Windows Server 2008 (or later), my deployment machine is Windows Server 2003.

Using a command-line zip tool:

function create-zip([String] $aDirectory, [String] $aZipfile)  
{  
  [string]$PathToZipExe = "K:\zip.exe";  
  & $PathToZipExe "-r" $aZipfile $aDirectory;  
}

create-zip "h:\Backup\*.*" "K:\test.zip"

I downloaded info-zip and called it with the source and destination locations as parameters.
This worked fine & was very easy to set up, but required an external dependency.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .