zip.ps1

Set-StrictMode -Version 2

function Expand-Zip {
    <#
    .Synopsis
        Extracts files or directories from a zip folder
    .Parameter Path
        The path to the Zip folder to extract
    .Parameter Destination
        The location to extract the files to
    .Parameter ZipDirectory
        The directory within the zip folder to extract. If not specified, extracts the whole zip file
    .Parameter ZipFileName
        The name of a specific file within ZipDirectory to extract
    .Example
        Expand-Zip c:\sample.zip c:\files\
         
        Description
        -----------
        This command extracts the entire contents of c:\sample.zip to c:\files\
    .Example
        Expand-Zip c:\sample.zip c:\sample\web\ -ZipDirectory web
         
        Description
        -----------
        This command extracts the contents of the web directory of c:\sample.zip to c:\sample\web
    .Example
        Expand-Zip c:\sample.zip c:\test\ -ZipDirectory documentation -zipFileName sample.txt
         
        Description
        -----------
        This command extracts the sample.txt file from the web directory of c:\sample.zip to c:\sample\sample.txt
    #>

    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({Test-Zip $_ })]
        [string]$Path,
        [parameter(Mandatory=$true, Position=1)]
        [ValidateNotNullOrEmpty()]
        [string]$Destination,
        [ValidateScript({!$_ -or (Test-Path $_ -PathType Container -IsValid)})]
        [string]$ZipDirectory,
        [ValidateScript({!$_ -or (Test-Path $_ -PathType Leaf -IsValid)})]
        [string]$ZipFileName
    )

    $prefix = ''
    if($ZipDirectory){
        $prefix = ($ZipDirectory).Replace('\','/').Trim('/') + '/'
    }
    if (!(test-path $Destination -PathType Container)) {
        New-item $Destination -Type Directory | out-null
    }

    #Convert path requried to ensure
    $absoluteDestination = (Resolve-Path $Destination).ProviderPath
    $zipAbsolutePath = (Resolve-Path $Path).ProviderPath

    $zipPackage = [IO.Compression.ZipFile]::OpenRead($zipAbsolutePath)
    try {
        $entries = $zipPackage.Entries
        if ($ZipFileName){
            $entries = $entries |
                ? {$_.FullName.Replace('\','/') -eq "${prefix}${ZipFileName}"} |
                select -First 1
        }
        else {
            #Filter out directories
            $entries = $zipPackage.Entries |? Name
            if ($ZipDirectory) {
                #Filter out items not under requested directory
                $entries = $entries |? { $_.FullName.Replace('\','/').StartsWith($prefix, "OrdinalIgnoreCase")}
            }
        }

        $totalFileSize = ($entries |% length | Measure-Object -sum).Sum
        $processedFileSize = 0
        $entries |% {
            $destination = join-path $absoluteDestination $_.FullName.Substring($prefix.Length)
            <#
            Write-Progress 'Extracting Zip' `
                -CurrentOperation $_.FullName `
                -PercentComplete ($processedFileSize / $totalFileSize * 100)
            #>

                      
            $itemDir = split-path $Destination -Parent
            if (!(Test-Path $itemDir -PathType Container)) {
                New-item $itemDir -Type Directory | out-null
            }
            [IO.Compression.ZipFileExtensions]::ExtractToFile($_, $Destination, $true)

            $processedFileSize += $_.Length
        }
        #Write-Progress 'Extracting-Zip' -Completed
        
    }
    finally {
        $zipPackage.Dispose()
    }
}


function Test-Zip {
    <#
    .Synopsis
        Tests whether a file exists and is a valid zip file.
 
    .Parameter Path
        The path to the file to test
 
    .Example
        Test-Zip c:\sample.zip
         
        Description
        -----------
        This command checks if the file c:\sample.zip exists
    #>

    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Path
    )

    Test-Path $Path -PathType Leaf
    if((Get-Item $Path).Extension -ne '.zip') {
        throw "$Path is not a zip file"
    }
}