PwSh.Fw.Archive.psm1

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

<#
.SYNOPSIS
List the files contained in an archive
 
.DESCRIPTION
Long description
 
.PARAMETER Path
Full path to the archive file
 
.PARAMETER PassThru
If specified, Get-ArchiveContent will return whole archive file object.
If not specified, Get-ArchiveContent will return file list
 
.EXAMPLE
archive.zip | Get-ArchiveContent
 
.NOTES
Inspired by @url https://stackoverflow.com/questions/43715949/powershell-extract-specific-files-folders-from-a-zipped-archive
#>

function Get-ArchiveContent {
    [CmdletBinding()]
    [OutputType([String])]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$Path,
        [switch]$PassThru
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        if (!(Test-FileExist $Path)) {
            Write-Fatal "Archive '$Path' not found."
        }
        # $archive = Get-Item -Path $Path

        #extract list entries for dir myzipdir/c/ into myzipdir.zip
        $zip = [IO.Compression.ZipFile]::OpenRead($Path)
        $entries = $zip.Entries
        #free object
        $zip.Dispose()

        if ($PassThru) {
            return $entries
        } else {
            return $entries.fullname
        }
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Expand a single from an archive
 
.DESCRIPTION
Extract only one specified file from an archive.
 
.PARAMETER Path
Path to the archive file
 
.PARAMETER DestinationPath
Destination path where to extract files
 
.PARAMETER Files
Exact filename to extract. The path must be relative to the root of the archive.
Wildcard are not allowed
 
.PARAMETER Force
If specified, erase existing files
 
.PARAMETER PassThru
If specified, return filesystem object of files extracted.
If not specified, return a list of path/files extracted
 
.EXAMPLE
"archive.zip" | Expand-SingleFileFromArchive -File "README.md"
 
.EXAMPLE
"archive.zip" | Expand-SingleFileFromArchive -File "src/path/to/file.c"
 
This example extract the file 'file.c' from the "archive.zip" file, located in the "src/path/to" folder inside the archive.
 
.NOTES
Inspired by @url https://stackoverflow.com/questions/43715949/powershell-extract-specific-files-folders-from-a-zipped-archive
#>

function Expand-SingleFileFromArchive {
    [CmdletBinding()]
    [OutputType([Boolean], [System.IO.FileInfo])]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$Path,
        [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$DestinationPath,
        [Parameter(Mandatory = $true, ValueFromPipeLine = $false)][string]$File,
        [switch]$Force,
        [switch]$PassThru
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        $rc = $false
        if (!(Test-FileExist $Path)) {
            Write-Fatal "Archive '$Path' not found."
        }
        $archive = Get-Item -Path $Path
        if ([string]::IsNullOrEmpty($DestinationPath)) {
            $DestinationPath = "$($archive.Directory)/$($archive.BaseName)"
        }
        #create dir for result of extraction
        $FileName = Split-Path $File -Leaf
        if ($Force) {
            if (Test-FileExist "$DestinationPath/$FileName") { Remove-Item -Path "$DestinationPath/$FileName" -Confirm:$false -Force:$Force }
            $null = New-Item -ItemType Directory -Path "$DestinationPath" -Force:$Force -ErrorAction SilentlyContinue
        }

        #extract list entries for dir myzipdir/c/ into myzipdir.zip
        try {
            $zip = [IO.Compression.ZipFile]::OpenRead($Path)
            $entry = $zip.GetEntry($File)
            if ($null -ne $entry) {
                #extraction
                [IO.Compression.ZipFileExtensions]::ExtractToFile( $entry, "$DestinationPath/$($entry.Name)", $Force)
                $rc = $?
            } else {
                eerror "File '$File' not found in archive '$($archive.Name)'"
                eerror "Try to Get-ArchiveContent to see what's inside."
                eerror "Remember, -File must be the full path to file from the archive point of view."
                $rc = $false
            }
        } catch {
            eerror $_
            $rc = $false
        } finally {
            #free object
            $zip.Dispose()
        }

        if ($PassThru) {
            if ($rc) { return "$DestinationPath/$($entry.Name)" } else { return $rc }
        } else {
            return $rc
        }
    }

    End {
        Write-LeaveFunction
    }
}