Out-Zip.ps1

function Out-Zip
{
    <#
    .Synopsis
        Outputs files into a zip
    .Description
        Stores files in a zip archive
    .Example
        dir -recurse | Out-Zip -ZipFile ".\a.zip"
    .Example
        dir $home\Documents\WindowsPowerShell\Modules\Pipeworks -Recurse |
            Out-Zip -ZipFile $home\Pipeworks.zip
        Expand-Zip $home\Pipeworks.zip -OutputPath $psHome\Modules\Pipeworks
    .Link
        Expand-Zip
    #>

    
    [OutputType([IO.Fileinfo])]
    param(
    # The path to a file.
    [Parameter(Mandatory=$true,
        ParameterSetName='FileList',
        Position=0,
        ValueFromPipelineByPropertyName=$true)]
    [Alias('Fullname')]
    [string[]]$FilePath,
    
    # The output zip file
    [Parameter(Mandatory=$true,Position=1,ValueFromPipelineByPropertyName=$true)]
    [string]$ZipFile,
    
    # If set, will not show progress. This improves performance and is good to include when calling this within another command.
    [Switch]$HideProgress,
    
    # The common root
    [string]$CommonRoot
    )
    
    begin {
    
        $zipFiles = New-Object Collections.ArrayList
        $fileList = New-Object Collections.ArrayList
        
        if (-not $script:cachedContentTypes) {
            $script:cachedContentTypes = @{}
            $ctKey = [Microsoft.Win32.Registry]::ClassesRoot.OpenSubKey("MIME\Database\Content Type")
            $ctKey.GetSubKeyNames() |
                ForEach-Object {
                    $extension= $ctKey.OpenSubKey($_).GetValue("Extension") 
                    if ($extension) {
                        $script:cachedContentTypes["${extension}"] = $_
                    }
                }

        }

        Add-Type -AssemblyName WindowsBase        
       
    }
    
    process {
        # Cool trick: Skip piped in directories by looking @ $_, which will contain the full bound object
        if ($_.PSIsContainer) { return } 
        foreach ($f in $filePath) {
            if ($f) {
                $null = $fileList.Add($f)
            }
        }       
        
        
        if ($zipFile) {
            $null  = $zipFiles.Add($zipFile) 
        }
    }
    
    end {                
        if (-not $commonRoot) {
            $commonRoot = ""
        }
        
        foreach ($f in $fileList) {
            if (-not $commonRoot) {
                $commonRoot = $f.Substring(0, $f.LastIndexOf("\"))
                continue
            }
            
            if ($f -like "${commonRoot}*") {
                continue
            } else {
                while ($commonRoot -and $f -notlike "${CommonRoot}*") {
                    $commonRoot = try { $commonRoot.Substring(0, $commonRoot.LastIndexOf("\")) } catch { ""}
                }
            }                             
        }
       
        $bufferSize = 1kb
        $zipFiles = $zipFiles | Select-Object -Unique
        $progressId = Get-Random
        foreach ($zf in $zipFiles) {
            $fullzf = "$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($zf))"
            
            if (-not $fullZf) { return } 
            if (-not $HideProgress) {
                Write-Progress "Creating Zip: $fullZf" " " -PercentComplete 1 -Id $progressId
            }
            
            $package = [IO.Packaging.ZipPackage]::Open($fullzf, "Create", "ReadWrite")
            
            # $fileList = $fileList | Select-Object -First 1
            $count = @($fileList).Count
            $n = 0
            foreach ($f in $fileList) {
                
                $rp = try { "$($ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($f))" } catch { }
                
                if ($rp) {
                    # Put the file in the package
                    $extension = [IO.Path]::GetExtension($rp)
                    
                    $mimeType = $script:CachedContentTypes[$extension]
                    if (-not $mimeType) {
                        $mimetype = "unknown/unknown"
                    }
                    $uri = $rp.Replace($commonRoot, "").Replace("\", "/")
                    $uri = [Web.HttpUtility]::UrlEncode($uri) -replace 
                        "%2f", "/" -replace "%27", "'"
                    $packagePart = try {
                        $package.CreatePart($uri, $mimetype, "Maximum")
                    } catch {
                        Write-Error -Message "Could Not Pack up $uri" -TargetObject $_ -Exception $_.Exception 
                    }
                    $streamPart = New-Object IO.StreamWriter $packagePart.GetStream("Create","Write")
                    $perc = $n * 100 / $count
                    if (-not $HideProgress) {
                        Write-Progress "Creating Zip: $fullZf" "Reading $rp" -PercentComplete $perc -Id $progressId
                    }
                    
                    $fileBytes= [IO.File]::ReadAllBytes($rp)                    

                    if (-not $HideProgress) {                    
                        Write-Progress "Creating Zip: $fullZf" "Compressing $rp" -PercentComplete $perc -Id $progressId
                    }
                    $write=  $streamPart.basestream.Write($fileBytes, 0, $fileBytes.Count)
                    
                    $streamPart.Close()
                    $package.Flush()

                }
                $n++
                
            }
            
            $package.Close()        
            if (-not $HideProgress) {
                Write-Progress "Creating Zip" "Completed" -Completed -Id $progressId
            }
            
         
            
        }
        
        
        
        
    }
}