Downloading files from the internet in PowerShell (with progress)


Ok, so the easiest way (that I know of) to download files in powershell from the internet is to use the .net WebClient.  The simple way I started with was the two liner:


$client = New-Object "System.Net.WebClient"
$client.DownloadFile("http://somesite.com/largefile.zip","c:\temp\largefile.zip"


However I was working on a script that required some pretty large files to be downloaded, and using the DownloadFile method has no progress indicator.  I figured some users might thing the program died.  So I decided to create a method that still uses the webclient to download the files, however give a status of where it is in the download.  Here is what I came up with, hopefully someone else will find it useful.


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
    }

    "`nFinished Download"

    $targetStream
.Flush()
    $targetStream.Close()
   
$targetStream.Dispose()
   
$responseStream.Dispose()
}


This would be used like
downloadFile "http://somesite/largefile.zip" "c:\temp\largefile.zip"


 

Comments (5)

  1. lime says:

    Hey, thanks! Good function. I've modified the code a bit, so that it would support the 'Write-Progress' cmdlet:

    function DownloadFile($url, $targetFile)

    {

       $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)

       {

           $targetStream.Write($buffer, 0, $count)

           $count = $responseStream.Read($buffer,0,$buffer.length)

           $downloadedBytes = $downloadedBytes + $count

           Write-Progress -activity "Downloading file '$($url.split('/') | Select -Last 1)'" -status "Downloaded ($([System.Math]::Floor($downloadedBytes/1024))K of $($totalLength)K): " -PercentComplete ((([System.Math]::Floor($downloadedBytes/1024)) / $totalLength)  * 100)

       }

       Write-Progress -activity "Finished downloading file '$($url.split('/') | Select -Last 1)'"

       $targetStream.Flush()

       $targetStream.Close()

       $targetStream.Dispose()

       $responseStream.Dispose()

    }

  2. Dennis Gaida says:

    Awesome with the Write-Progress addition! Thx lime!

  3. Jimmy says:

    Nice bit of code that, thanks guys!

  4. Dscoduc says:

    Problem with the code when the download file is less than 1024 bytes.  I corrected by doing an If/else statement that checks for file length less than 1024:

    $responseContentLength = $response.get_ContentLength()

    if(-not ($responseContentLength -lt 1024))

    {

       $totalLength = [System.Math]::Floor($response.get_ContentLength()/1024)

    }

    else

    {

       $totalLength = [System.Math]::Floor(1024/1024)

    }

  5. glenn says:

    I'm getting an exception error

    You cannot call a method on a null-valued expression.

    At line:18 char:9

    +         $targetStream.Write($buffer, 0, $count)

    +         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

       + FullyQualifiedErrorId : InvokeMethodOnNull

    Exception setting "CursorLeft": "The handle is invalid.

    "

    At line:16 char:9

    +         [System.Console]::CursorLeft = 0

    +         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : NotSpecified: (:) [], SetValueInvocationException

       + FullyQualifiedErrorId : ExceptionWhenSetting

Skip to main content