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?

SteB
  • 989
  • 6
  • 15
  • 31
  • 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 Dec 07 '12 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 Dec 07 '12 at 15:58
  • [See the bottom of this answer](http://serverfault.com/a/448743/10472) for how to do this in PS3 – MDMarra Dec 07 '12 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 Dec 07 '12 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 Dec 07 '12 at 16:30
  • @MDMarra - Only up to 4, I'll install 4.5 and try again, thanks – SteB Dec 07 '12 at 16:33
  • @MDMarra - that works great, thanks. I've just realised that .Net 4.5 isn't supported on Server 2003! I might try calling a zip utility from the command-line, thanks for all your efforts. – SteB Dec 07 '12 at 16:46
  • There is a ...messier... way to do this by forcing the file type. It will work on 2003 and is outlined in the same link I posted. – MDMarra Dec 07 '12 at 16:56

2 Answers2

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
    }
}
nimizen
  • 276
  • 3
  • 9
  • 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 Dec 10 '12 at 09: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 Dec 10 '12 at 10:09
  • I've updated my answer - the $zipFilepath & $zipFilename weren't being joined correctly. – nimizen Dec 10 '12 at 10:38
  • Sorry, it was my fault, I'd accidentally changed it to Test-Path-Path! That works great now. – SteB Dec 10 '12 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.

SteB
  • 989
  • 6
  • 15
  • 31