261
votes

Is it possible to create a zip archive using PowerShell?

23
any sample with full source code ?Kiquenet

23 Answers

126
votes

If you head on over to CodePlex and grab the PowerShell Community Extensions, you can use their write-zip cmdlet.

Since

CodePlex is in read-only mode in preparation for shutdown

you can go to PowerShell Gallery.

263
votes

PowerShell v5.0 adds Compress-Archive and Expand-Archive cmdlets. The linked pages have full examples, but the gist of it is:

# Create a zip file with the contents of C:\Stuff\
Compress-Archive -Path C:\Stuff -DestinationPath archive.zip

# Add more files to the zip file
# (Existing files in the zip file with the same name are replaced)
Compress-Archive -Path C:\OtherStuff\*.txt -Update -DestinationPath archive.zip

# Extract the zip file to C:\Destination\
Expand-Archive -Path archive.zip -DestinationPath C:\Destination
258
votes

A pure PowerShell alternative that works with PowerShell 3 and .NET 4.5 (if you can use it):

function ZipFiles( $zipfilename, $sourcedir )
{
   Add-Type -Assembly System.IO.Compression.FileSystem
   $compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
   [System.IO.Compression.ZipFile]::CreateFromDirectory($sourcedir,
        $zipfilename, $compressionLevel, $false)
}

Just pass in the full path to the zip archive you would like to create and the full path to the directory containing the files you would like to zip.

57
votes

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.

UPDATE: See below for other developers that have expanded on this over the years...

45
votes

Install 7zip (or download the command line version instead) and use this PowerShell method:

function create-7zip([String] $aDirectory, [String] $aZipfile){
    [string]$pathToZipExe = "$($Env:ProgramFiles)\7-Zip\7z.exe";
    [Array]$arguments = "a", "-tzip", "$aZipfile", "$aDirectory", "-r";
    & $pathToZipExe $arguments;
}

You can the call it like this:

create-7zip "c:\temp\myFolder" "c:\temp\myFolder.zip"
19
votes

Lot has changed since the initial answer was posted. Here are some of the latest examples using Compress-Archive command.

Command to create new archive file, Draft.zip, by compressing two files, Draftdoc.docx and diagram2.vsd, specified by the Path parameter. The compression level specified for this operation is Optimal.

Compress-Archive -Path C:\Reference\Draftdoc.docx, C:\Reference\Images\diagram2.vsd -CompressionLevel Optimal -DestinationPath C:\Archives\Draft.Zip

Command to creates new archive file, Draft.zip, by compressing two files, Draft doc.docx and Diagram [2].vsd, specified by the LiteralPath parameter. The compression level specified for this operation is Optimal.

Compress-Archive -LiteralPath 'C:\Reference\Draft Doc.docx', 'C:\Reference\Images\Diagram [2].vsd'  -CompressionLevel Optimal -DestinationPath C:\Archives\Draft.Zip

Command to create new archive file, Draft.zip, in the C:\Archives folder. The new archive file contains every file in the C:\Reference folder, because a wildcard character was used in place of specific file names in the Path parameter.

Compress-Archive -Path C:\Reference\* -CompressionLevel Fastest -DestinationPath C:\Archives\Draft

Command creates an archive from an entire folder, C:\Reference

Compress-Archive -Path C:\Reference -DestinationPath C:\Archives\Draft

PowerShell appends the .zip extension to the file name automatically.

15
votes

Edit two - This code is an ugly, ugly kluge from olden days. You do not want it.

This compresses the contents of .\in 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()

Edit: Unreliable for larger files, maybe >10mb, YMMV. Something to do with appdomain evidence and isolated storage. The friendlier .NET 4.5 approach works nicely from PS v3, but wanted more memory in my case. To use .NET 4 from PS v2, config files need an unsupported tweak.

12
votes

Giving below another option. This will zip up a full folder and will write the archive to a given path with the given name.

Requires .NET 3 or above

Add-Type -assembly "system.io.compression.filesystem"

$source = 'Source path here'    
$destination = "c:\output\dummy.zip"

If(Test-path $destination) {Remove-item $destination}

[io.compression.zipfile]::CreateFromDirectory($Source, $destination)
11
votes

Here is a native solution for PowerShell v5, using the cmdlet Compress-Archive Creating Zip files using PowerShell.

See also the Microsoft Docs for Compress-Archive.

Example 1:

Compress-Archive `
    -LiteralPath C:\Reference\Draftdoc.docx, C:\Reference\Images\diagram2.vsd `
    -CompressionLevel Optimal `
    -DestinationPath C:\Archives\Draft.Zip

Example 2:

Compress-Archive `
    -Path C:\Reference\* `
    -CompressionLevel Fastest `
    -DestinationPath C:\Archives\Draft

Example 3:

Write-Output $files | Compress-Archive -DestinationPath $outzipfile
8
votes

What about System.IO.Packaging.ZipPackage?

It would require .NET 3.0 or greater.

#Load some assemblys. (No line break!)
[System.Reflection.Assembly]::Load("WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")

#Create a zip file named "MyZipFile.zip". (No line break!)
$ZipPackage=[System.IO.Packaging.ZipPackage]::Open("C:\MyZipFile.zip",
   [System.IO.FileMode]"OpenOrCreate", [System.IO.FileAccess]"ReadWrite")

#The files I want to add to my archive:
$files = @("/Penguins.jpg", "/Lighthouse.jpg")

#For each file you want to add, we must extract the bytes
#and add them to a part of the zip file.
ForEach ($file In $files)
{
   $partName=New-Object System.Uri($file, [System.UriKind]"Relative")
   #Create each part. (No line break!)
   $part=$ZipPackage.CreatePart($partName, "",
      [System.IO.Packaging.CompressionOption]"Maximum")
   $bytes=[System.IO.File]::ReadAllBytes($file)
   $stream=$part.GetStream()
   $stream.Write($bytes, 0, $bytes.Length)
   $stream.Close()
}

#Close the package when we're done.
$ZipPackage.Close()

via Anders Hesselbom

7
votes

For compression, I would use a library (7-Zip is good like Michal suggests).

If you install 7-Zip, the installed directory will contain 7z.exe which is a console application.
You can invoke it directly and use any compression option you want.

If you wish to engage with the DLL, that should also be possible.
7-Zip is freeware and open source.

6
votes

Why does no one look at the documentation? The same .NET 4.5 library everyone is referencing lets you do anything you want, including creating an empty ZIP and adding individual files to it.

See below for a code example:

# Load the .NET assembly
Add-Type -Assembly 'System.IO.Compression'
Add-Type -Assembly 'System.IO.Compression.FileSystem'

# Must be used for relative file locations with .NET functions instead of Set-Location:
[System.IO.Directory]::SetCurrentDirectory('.\Desktop')

# Create the zip file and open it:
$z = [System.IO.Compression.ZipFile]::Open('z.zip', [System.IO.Compression.ZipArchiveMode]::Create)

# Add a compressed file to the zip file:
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($z, 't.txt', 't.txt')

# Close the file
$z.Dispose()

Here's an overview on how to manipulate the zip archive while you're at it (just remember to close the file afterwards):

I encourage you to browse the documentation because that's how I found all this.

4
votes

This is really obscure but works. 7za.exe is standalone version of 7zip and is available with install package.

# get files to be send
$logFiles = Get-ChildItem C:\Logging\*.* -Include *.log | where {$_.Name -match $yesterday} 

foreach ($logFile in $logFiles)
{
    Write-Host ("Processing " + $logFile.FullName)

    # compress file
    & ./7za.exe a -mmt=off ($logFile.FullName + ".7z") $logFile.FullName

}
4
votes

If someone needs to zip a single file (and not a folder): http://blogs.msdn.com/b/jerrydixon/archive/2014/08/08/zipping-a-single-file-with-powershell.aspx

[CmdletBinding()]
Param(
     [Parameter(Mandatory=$True)]
     [ValidateScript({Test-Path -Path $_ -PathType Leaf})]
     [string]$sourceFile,

     [Parameter(Mandatory=$True)]
     [ValidateScript({-not(Test-Path -Path $_ -PathType Leaf)})]
     [string]$destinationFile
) 

<#
     .SYNOPSIS
     Creates a ZIP file that contains the specified innput file.

     .EXAMPLE
     FileZipper -sourceFile c:\test\inputfile.txt 
                -destinationFile c:\test\outputFile.zip
#> 

function New-Zip
{
     param([string]$zipfilename)
     set-content $zipfilename 
          ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
     (dir $zipfilename).IsReadOnly = $false
}

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
     }
}

dir $sourceFile | Add-Zip $destinationFile
4
votes

Here is the working code, zipping all files from a source folder and create a zip file in destination folder.

    $DestZip="C:\Destination\"
    $Source = "C:\Source\"

    $folder = Get-Item -Path $Source

    $ZipTimestamp = Get-Date -format yyyyMMdd-HHmmss;
    $ZipFileName  = $DestZip + "Backup_" + $folder.name + "_" + $ZipTimestamp + ".zip" 

    $Source

    set-content $ZipFileName ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18)) 
    # Wait for the zip file to be created.
    while (!(Test-Path -PathType leaf -Path $ZipFileName))
    {    
        Start-Sleep -Milliseconds 20
    } 
    $ZipFile = (new-object -com shell.application).NameSpace($ZipFileName)

    Write-Output (">> Waiting Compression : " + $ZipFileName)       

    #BACKUP - COPY
    $ZipFile.CopyHere($Source) 

    $ZipFileName
    # ARCHIVE

    Read-Host "Please Enter.."
4
votes
function Zip-File
    {
    param (
    [string]$ZipName,
    [string]$SourceDirectory 

    )
       Add-Type -Assembly System.IO.Compression.FileSystem
       $Compress = [System.IO.Compression.CompressionLevel]::Optimal
       [System.IO.Compression.ZipFile]::CreateFromDirectory($SourceDirectory,
            $ZipName, $Compress, $false)
    }

Note:
ZipName: Full Path of the Zip File which you want to create.

SourceDirectory: Full path to the directory containing the files which you would like to zip.

3
votes

In case you have WinRAR installed:

function ZipUsingRar([String] $directory, [String] $zipFileName)
{
  Write-Output "Performing operation ""Zip File"" on Target ""Item: $directory Destination:"
  Write-Output ($zipFileName + """")
  $pathToWinRar = "c:\Program Files\WinRAR\WinRar.exe";
  [Array]$arguments = "a", "-afzip", "-df", "-ep1", "$zipFileName", "$directory";
  & $pathToWinRar $arguments;
}

The meaning of the arguments: afzip creates zip archive, df deletes files, ep1 does not create full directory path within archive

3
votes

Here is a slightly improved version of sonjz's answer,it adds an overwrite option.

function Zip-Files(
        [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$false)]
        [string] $zipfilename,
        [Parameter(Position=1, Mandatory=$true, ValueFromPipeline=$false)]
        [string] $sourcedir,
        [Parameter(Position=2, Mandatory=$false, ValueFromPipeline=$false)]
        [bool] $overwrite)

{
   Add-Type -Assembly System.IO.Compression.FileSystem
   $compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal

    if ($overwrite -eq $true )
    {
        if (Test-Path $zipfilename)
        {
            Remove-Item $zipfilename
        }
    }

    [System.IO.Compression.ZipFile]::CreateFromDirectory($sourcedir, $zipfilename, $compressionLevel, $false)
}
3
votes

This should also work for compressing a single file without using a temp folder and using native .Net 4.5, converted from C# from this StackOverflow answer. It uses a nicer using syntax taken from here.

Usage:

ZipFiles -zipFilename output.zip -sourceFile input.sql -filename name.inside.zip.sql

Code:

function ZipFiles([string] $zipFilename, [string] $sourceFile, [string] $filename)
{
    $fullSourceFile = (Get-Item -Path "$sourceFile" -Verbose).FullName
    $fullZipFile = (Get-Item -Path "$zipFilename" -Verbose).FullName

    Add-Type -AssemblyName System.IO
    Add-Type -AssemblyName System.IO.Compression
    Add-Type -AssemblyName System.IO.Compression.FileSystem

    Using-Object ($fs = New-Object System.IO.FileStream($fullZipFile, [System.IO.FileMode]::Create)) {
         Using-Object ($arch = New-Object System.IO.Compression.ZipArchive($fs, [System.IO.Compression.ZipArchiveMode]::Create)) {
             [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($arch, $fullSourceFile, $filename)
        }
    }
}

Using:

function Using-Object
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowEmptyCollection()]
        [AllowNull()]
        [Object]
        $InputObject,

        [Parameter(Mandatory = $true)]
        [scriptblock]
        $ScriptBlock
    )

    try
    {
        . $ScriptBlock
    }
    finally
    {
        if ($null -ne $InputObject -and $InputObject -is [System.IDisposable])
        {
            $InputObject.Dispose()
        }
    }
}
2
votes

I use this snippet to check my database backups folder for backup files not compressed yet, compress them using 7-Zip, and finally deleting the *.bak files to save some disk space. Notice files are ordered by length (smallest to biggest) before compression to avoid some files not being compressed.

$bkdir = "E:\BackupsPWS"
$7Zip = 'C:\"Program Files"\7-Zip\7z.exe'

get-childitem -path $bkdir | Sort-Object length |
where
{
    $_.extension -match ".(bak)" -and
    -not (test-path ($_.fullname -replace "(bak)", "7z"))
} |
foreach
{
    $zipfilename = ($_.fullname -replace "bak", "7z")
    Invoke-Expression "$7Zip a $zipfilename $($_.FullName)"
}
get-childitem -path $bkdir |
where {
    $_.extension -match ".(bak)" -and
   (test-path ($_.fullname -replace "(bak)", "7z"))
} |
foreach { del $_.fullname }

Here you can check a PowerShell script to backup, compress and transfer those files over FTP.

2
votes

Here a complete command line example to launch from cmd.exe or from ssh or what you want !

powershell.exe -nologo -noprofile -command "&{ Add-Type -A 'System.IO.Compression.FileSystem' [System.IO.Compression.ZipFile]::CreateFromDirectory('c:/path/to/source/folder/', 'c:/path/to/output/file.zip');}"

Regards

2
votes

Loading the [System.IO.IOException] class and using its methods is an important step in order to suppress unwanted errors, due the fact that it's a class not native to PowerShell, so expect various contexts of errors without it.

I error-controlled my script to the T, but got a lot of extra red 'file exists' output while using [System.IO.Compression.ZipFile] class

function zipFiles(
    [Parameter(Position=0, Mandatory=$true]
    [string] $sourceFolder,
    [Parameter(Position=1, Mandatory=$true]
    [string]$zipFileName,
    [Parameter(Position=2, Mandatory=$false]
    [bool]$overwrite)

{   
Add-Type -Assembly System.IO
Add-Type -Assembly System.IO.Compression.FileSystem

$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal

$directoryTest = (Test-Path $dailyBackupDestFolder)
$fileTest = (Test-Path $zipFileName)

if ( $directoryTest -eq $false) 
{ 
    New-Item -ItemType Directory -Force -Path $dailyBackupDestFolder 
}

     if ( $fileTest -eq $true)
     {
           if ($overwrite -eq $true ){Remove-Item $zipFileName}
     }   


    try
    {
         [System.IO.Compression.ZipFile]::CreateFromDirectory($sourceFolder,$zipFileName,$compressionLevel)       

    }
    catch [System.IO.IOException] 
    {
       Write-Output ($dateTime + ' | ' + $_.Exception.Message ) | Out-File $logFile -append -force 
    }
} 

What I am doing here is catching these IO Errors, such as accessing files that exist already, catching that error and directing it to a logfile that I am maintaining with a larger program.

2
votes

Complete command-line Commands in Windows for Compressing and Extracting Directory is as follows:

  • For Compression:

    powershell.exe -nologo -noprofile -command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::CreateFromDirectory('C:\Indus','C:\Indus.zip'); }"
    
  • For Extracting:

    powershell.exe -nologo -noprofile -command "& { Add-Type -A 'System.IO.Compression.FileSystem';[IO.Compression.ZipFile]::ExtractToDirectory('C:\Indus.zip','C:\Indus'); }"