PowerShell in Action: Size Measurement and Download Timing for File Management

In this blog post, we will explore a PowerShell script designed to efficiently manage file downloads by measuring file sizes without requiring the actual download. By utilizing Invoke-WebRequest, we can obtain file size information through HTTP headers, ensuring that users have accurate expectations before initiating a download. The script also compares download times using both Invoke-WebRequest and Start-BitsTransfer, providing valuable insights into the performance of each method. Additionally, we will implement a cleanup process for any downloaded files to maintain an organized environment.

Measuring File Sizes via HTTP Headers

First we define the download URLs were making the request to. Then we create a function Get-FileSize that takes a URL as a parameter. We can use the returned object from Invoke-WebRequest to extract the file size in the request header object. You can read about formatting numerical output here.

# download URLs
$chromeUrl = "https://dl.google.com/dl/chrome/install/googlechromestandaloneenterprise64.msi"
$sevenZipUrl = "https://www.7-zip.org/a/7z2408-x64.msi"

function Get-FileSize {
    param (
        $url
    )
    # Get the sizes of the file without downloading through the returned header object
    $headers = Invoke-WebRequest -Uri $url -Method Head
    # size in bytes
    $sb = $headers.headers['content-length']

    # 1GB, 1MB, 1KB are size constants
    switch ($true) {
        # order from largest to smallest for correct sequential conditional logic
        ($sb/1GB -ge 1) { $readableSize = "{0:N2} GB" -f ($sb / 1GB); break }
        ($sb/1MB -ge 1) { $readableSize = "{0:N2} MB" -f ($sb / 1MB); break }
        ($sb/1KB -ge 1) { $readableSize = "{0:N2} KB" -f ($sb / 1KB); break }
        default { $readableSize = "$_ Bytes"; break }
    }
    return $readableSize
}


$chromeSize = Get-FileSize -url $chromeUrl
$sevenZSize = Get-FileSize -url $sevenZipUrl

Write-Output("Chrome File size: {0}`n7-Zip File size: {1}" -f $chromeSize, $sevenZSize)

A PowerShell Function to Compare Download Times: Invoke-WebRequest vs. Start-BitsTransfer

We create a Measure-RequestTimeInMilliSeconds function in PowerShell that allows users to efficiently compare the download times of files using both Invoke-WebRequest and Start-BitsTransfer. By accepting a URL and a desired file name, the function measures how long each method takes to download the specified file. It leverages the Measure-Command cmdlet to capture the execution time in milliseconds, providing precise metrics for performance evaluation. The results are returned as a PowerShell object, making it easy to access and display the timing information. We will use this as a helper function when writing the output.

function Measure-RequestTimeInMilliSeconds {
    param (
        $url,
        $fileName
    )
    # get the file extension from the URL
    $fileExtension = $url.Split(".")[-1]

    # https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/measure-command?view=powershell-7.4 
    $timeIWR = Measure-Command {
        (Invoke-WebRequest $url -OutFile "$($fileName)IWR.$fileExtension")
    }

    $timeBITS = Measure-Command {
        (Invoke-WebRequest $url -OutFile "$($fileName)BITS.$fileExtension")
    }
    return [psobject]@{
        "iwr" = $timeIWR.TotalMilliseconds;
        "bits" = $timeBITS.TotalMilliseconds;
    }
}

Displaying Download Times with a PowerShell Function for Timed Output

First, we define the Write-TimedOutput function, which takes a URL and a name as parameters. This function calls Measure-RequestTimeInMilliSeconds to measure the download times for both Invoke-WebRequest and Start-BitsTransfer. The results are then displayed, showing the time taken for each method in a clear output format. Finally, any temporary files created during the download process are removed to maintain a clean environment.

function Write-TimedOutput {
    param (
        $url,
        $name
    )

    $timedRequest = Measure-RequestTimeInMilliSeconds -url $url -fileName $name

    Write-Output("Time $name with Invoke-WebRequest: {0}" -f $timedRequest.iwr)
    Write-Output("Time $name with Start-BitsTransfer: {0}" -f $timedRequest.bits)

    Remove-Item -path "*.$($url.Split(".")[-1])" -Force | Out-Null
}

Write-TimedOutput -url $sevenZipUrl -name "7-Zip"
Write-TimedOutput -url $chromeUrl -name "ChromeEnterprise"

Completed Script

$chromeUrl = "https://dl.google.com/dl/chrome/install/googlechromestandaloneenterprise64.msi"
$sevenZipUrl = "https://www.7-zip.org/a/7z2408-x64.msi"

function Get-FileSize {
    param (
        $url
    )
    $headers = Invoke-WebRequest -Uri $url -Method Head
    $sb = $headers.headers['content-length']


    switch ($true) {
        
        ($sb/1GB -ge 1) { $readableSize = "{0:N2} GB" -f ($sb / 1GB); break }
        ($sb/1MB -ge 1) { $readableSize = "{0:N2} MB" -f ($sb / 1MB); break }
        ($sb/1KB -ge 1) { $readableSize = "{0:N2} KB" -f ($sb / 1KB); break }
        default { $readableSize = "$_ Bytes"; break }
    }
    return $readableSize
}


$chromeSize = Get-FileSize -url $chromeUrl
$sevenZSize = Get-FileSize -url $sevenZipUrl

Write-Output("Chrome File size: {0}`n7-Zip File size: {1}" -f $chromeSize, $sevenZSize)


function Measure-RequestTimeInMilliSeconds {
    param (
        $url,
        $fileName
    )

    $fileExtension = $url.Split(".")[-1]
    $timeIWR = Measure-Command {
        (Invoke-WebRequest $url -OutFile "$($fileName)IWR.$fileExtension")
    }

    $timeBITS = Measure-Command {
        (Invoke-WebRequest $url -OutFile "$($fileName)BITS.$fileExtension")
    }
    return [psobject]@{
        "iwr" = $timeIWR.TotalMilliseconds;
        "bits" = $timeBITS.TotalMilliseconds;
    }
}

function Write-TimedOutput {
    param (
        $url,
        $name
    )

    $timedRequest = Measure-RequestTimeInMilliSeconds -url $url -fileName $name

    Write-Output("Time $name with Invoke-WebRequest: {0}" -f $timedRequest.iwr)
    Write-Output("Time $name with Start-BitsTransfer: {0}" -f $timedRequest.bits)

    Remove-Item -path "*.$($url.Split(".")[-1])" -Force | Out-Null
}

Write-TimedOutput -url $sevenZipUrl -name "7-Zip"
Write-TimedOutput -url $chromeUrl -name "ChromeEnterprise"

Conclusion

In this PowerShell script, we began by defining the download URLs for Google Chrome and 7-Zip. We created a function, Get-FileSize, that retrieves and formats the file sizes without downloading them, allowing us to assess the data beforehand. Next, we implemented Measure-RequestTimeInMilliSeconds to compare the download times using both Invoke-WebRequest and Start-BitsTransfer, where we found that Start-BitsTransfer was slightly faster. Furthermore, the Background Intelligent Transfer Service (BITS) supports asynchronous transfers and can wait for system resources to become available, which is beneficial for managing larger files or slower connections. During these transfers, BITS creates temporary files in a designated directory, which are used to store partially downloaded data, ensuring that transfers can be resumed without loss of progress. Finally, the Write-TimedOutput function displayed the download times for each method while ensuring any temporary files were cleaned up afterward. Overall, this script enhances our ability to manage file downloads efficiently, balancing speed and size considerations effectively, and it allows for running simulations to further analyze performance

Leave a Reply