Downloading large files in Windows command prompt / PowerShell Downloading large files in Windows command prompt / PowerShell powershell powershell

Downloading large files in Windows command prompt / PowerShell


After some research I found @MichaelS's method the simplest, nevertheless I added some modifications that you could find useful, as such:

  1. Added Try/Finally blocks, so we could handle user interruptions (Ctrl+C) and cleanup (See Gracefully stopping in Powershell)
  2. Utilize Write-Progress functionality, so it feels more like Start-BitsTransfer
  3. Buffer size optimization, atleast for my particular connection/machine (see https://www.microsoft.com/en-us/research/wp-content/uploads/2004/12/tr-2004-136.pdf, cf. https://stackoverflow.com/a/3034155/10504393)

In action it looks like on that gif.

function Get-FileFromURL {    [CmdletBinding()]    param(        [Parameter(Mandatory, Position = 0)]        [System.Uri]$URL,        [Parameter(Mandatory, Position = 1)]        [string]$Filename    )    process {        try {            $request = [System.Net.HttpWebRequest]::Create($URL)            $request.set_Timeout(5000) # 5 second timeout            $response = $request.GetResponse()            $total_bytes = $response.ContentLength            $response_stream = $response.GetResponseStream()            try {                # 256KB works better on my machine for 1GB and 10GB files                # See https://www.microsoft.com/en-us/research/wp-content/uploads/2004/12/tr-2004-136.pdf                # Cf. https://stackoverflow.com/a/3034155/10504393                $buffer = New-Object -TypeName byte[] -ArgumentList 256KB                $target_stream = [System.IO.File]::Create($Filename)                $timer = New-Object -TypeName timers.timer                $timer.Interval = 1000 # Update progress every second                $timer_event = Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action {                    $Global:update_progress = $true                }                $timer.Start()                do {                    $count = $response_stream.Read($buffer, 0, $buffer.length)                    $target_stream.Write($buffer, 0, $count)                    $downloaded_bytes = $downloaded_bytes + $count                    if ($Global:update_progress) {                        $percent = $downloaded_bytes / $total_bytes                        $status = @{                            completed  = "{0,6:p2} Completed" -f $percent                            downloaded = "{0:n0} MB of {1:n0} MB" -f ($downloaded_bytes / 1MB), ($total_bytes / 1MB)                            speed      = "{0,7:n0} KB/s" -f (($downloaded_bytes - $prev_downloaded_bytes) / 1KB)                            eta        = "eta {0:hh\:mm\:ss}" -f (New-TimeSpan -Seconds (($total_bytes - $downloaded_bytes) / ($downloaded_bytes - $prev_downloaded_bytes)))                        }                        $progress_args = @{                            Activity        = "Downloading $URL"                            Status          = "$($status.completed) ($($status.downloaded)) $($status.speed) $($status.eta)"                            PercentComplete = $percent * 100                        }                        Write-Progress @progress_args                        $prev_downloaded_bytes = $downloaded_bytes                        $Global:update_progress = $false                    }                } while ($count -gt 0)            }            finally {                if ($timer) { $timer.Stop() }                if ($timer_event) { Unregister-Event -SubscriptionId $timer_event.Id }                if ($target_stream) { $target_stream.Dispose() }                # If file exists and $count is not zero or $null, than script was interrupted by user                if ((Test-Path $Filename) -and $count) { Remove-Item -Path $Filename }            }        }        finally {            if ($response) { $response.Dispose() }            if ($response_stream) { $response_stream.Dispose() }        }    }}


Not sure where I've got this piece of code from originally but I've modified it several times. Hope this will help you.

function downloadFile($url, $targetFile){    "Downloading $url"    $uri = New-Object "System.Uri" "$url"    $request = [System.Net.HttpWebRequest]::Create($uri)    $request.set_Timeout(15000) #15 second timeout    $response = $request.GetResponse()    $totalLength = [System.Math]::Floor($response.get_ContentLength()/1024)    $responseStream = $response.GetResponseStream()    $targetStream = New-Object -TypeName System.IO.FileStream -ArgumentList $targetFile, Create    $buffer = new-object byte[] 10KB    $count = $responseStream.Read($buffer,0,$buffer.length)    $downloadedBytes = $count    while ($count -gt 0)    {        [System.Console]::CursorLeft = 0        [System.Console]::Write("Downloaded {0}K of {1}K", [System.Math]::Floor($downloadedBytes/1024), $totalLength)        $targetStream.Write($buffer, 0, $count)        $count = $responseStream.Read($buffer,0,$buffer.length)        $downloadedBytes = $downloadedBytes + $count    }    "Finished Download"    $targetStream.Flush()    $targetStream.Close()    $targetStream.Dispose()    $responseStream.Dispose()}downloadFile "http://URL_to_your_file" "C:\Path\to\destination.file"