Home > Mobile >  Calling a Function During Array Creation to export contents of folder containing Zip files
Calling a Function During Array Creation to export contents of folder containing Zip files

Time:02-13

I am having trouble trying to call a function for a script that I'm using to build a list of zip files in a folder on my PC. The final CSV I need is to create is a list of the zip files with their uncompressed sizes. Here is what I have so far (compiled from several posts):

Function to get the uncompressed size:

function Get-UncompressedZipFileSize {

param (
    $Path
)

$shell = New-Object -ComObject shell.application
$zip = $shell.NameSpace($Path)
$size = 0
foreach ($item in $zip.items()) {
    if ($item.IsFolder) {
        $size  = Get-UncompressedZipFileSize -Path $item.Path
    } else {
        $size  = $item.size
    }
}


[System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$shell) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

return $size
}

Here is my Array Creation:

$arr = @()
gci C:\zips -recurse | ? {$_.PSIsContainer -eq $False} | % {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty Name $_.Name
$obj | Add-Member NoteProperty FullPath $_.FullName
$arr  = $obj
}
$arr | Export-CSV -notypeinformation c:\zips

I'm stuck at creating a new member object into my array that will call the get-uncompressedzipfilesize function to pass that size back into the array as a new column in my zip. Is something like this even possible.?

CodePudding user response:

To make this simpler, since you're only calling your function to get the size of the current folder (zip), you can use a Calculated Property for this:

$Path = "C:\Zips"
Get-ChildItem -Path $Path -Directory -Recurse | 
    Select-Object -Property Name, FullName,
    @{
        Name = "Size"
        Expression = {
            Get-UncompressedZipFileSize -Path $_.FullName
        }
    } | Export-Csv -Path "$Path\zip.csv" -Force -NoTypeInformation -Append 

On another note, if you ever find yourself explicitly adding to an array, take advantage of PowerShell's pipeline streaming.

$Path = "C:\Zips"
Get-ChildItem -Path $Path -Directory -Recurse | 
    ForEach-Object -Process {
        [PSCustomObject]@{
            Name = $_.Name
            FullPath = $_.FullName
            Size = Get-UncompressedZipFileSize -Path $_.FullName
        } | Export-Csv -Path "$Path\zip.csv" -Force -NoTypeInformation -Append 
    }

Not only is adding to a fixed array ( =) computationally expensive (if you have a large directory), it is slow. Fixed arrays mean just that, they are a fixed size and in order for you to add to it, it needs to be broken down and recreated. An alternate solution would by an arraylist but, in this case - and in most cases - it's not needed.

  • Get-ChildItem also includes a -Directory switch to search for just folders. Presented in V3.
  • I would recommend searching for the file extension of the compressed folders as well so you don't run into any issues using -Filter.

CodePudding user response:

Here is an alternative using ZipFile Class. The SizeConvert function is inspired from this answer. The output of the Get-ZipFileSize would be the absolute path of the Zip File, it's compressed and expanded size and it's formatted sizes (i.e.: 7.88 MB instead of 8262942).

using namespace System.IO
using namespace System.IO.Compression
using namespace System.Linq

function SizeConvert {
[cmdletbinding()]
param(
    [object]$Length,
    [int]$DecimalPoints = 2
)

    $suffix = "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
    $index = 0
    while ($Length -ge 1kb) {
        $Length /= 1kb
        $index  
    }
    return [string]::Format(
        '{0} {1}',
        [math]::Round($Length, $DecimalPoints), $suffix[$index]
    )
}

function Get-ZipFileSize {
[cmdletbinding()]
param(
    [parameter(ValueFromPipeline)]
    [FileInfo]$Path
)
    process {
        try
        {
            $zip = [ZipFile]::OpenRead($Path.FullName)
            $compressedSize = [Enumerable]::Sum([int64[]]$zip.Entries.CompressedLength)
            $expandedSize = [Enumerable]::Sum([Int64[]]$zip.Entries.Length)

            [pscustomobject]@{
                FilePath            = $Path.FullName
                RawExpanded         = $expandedSize
                RawCompressed       = $compressedSize
                FormattedExpanded   = SizeConvert $expandedSize -DecimalPoints 3
                FormattedCompressed = SizeConvert $compressedSize -DecimalPoints 3
            }
        }
        catch
        {
            $PSCmdlet.WriteError($_)
        }
        finally
        {
            if($zip -is [System.IDisposable]) {
                $zip.Dispose()
            }
        }
    }
}

Get-ChildItem -Filter *.zip -Recurse | Get-ZipFileSize | Export-Csv ....
  • Related