@@ -11,7 +11,7 @@ function Save-WebFile {
1111 [OutputType ([System.IO.FileInfo ])]
1212 param
1313 (
14- [Parameter (Position = 0 , Mandatory , ValueFromPipelineByPropertyName )]
14+ [Parameter (Position = 0 , Mandatory , ValueFromPipelineByPropertyName )]
1515 [Alias (' FileUri' )]
1616 [System.String ]
1717 $SourceUrl ,
@@ -44,8 +44,7 @@ function Save-WebFile {
4444 # =================================================
4545 # DestinationDirectory
4646 # =================================================
47- if (Test-Path " $DestinationDirectory " )
48- {
47+ if (Test-Path " $DestinationDirectory " ) {
4948 Write-Verbose " Directory already exists at $DestinationDirectory "
5049 }
5150 else {
@@ -62,8 +61,8 @@ function Save-WebFile {
6261 Remove-Item - Path $DestinationNewItem.FullName - Force | Out-Null
6362 }
6463 else {
65- Write-Warning " Unable to write to Destination Directory"
66- Break
64+ Write-Warning ' Unable to write to Destination Directory'
65+ break
6766 }
6867 # =================================================
6968 # DestinationName
@@ -83,8 +82,8 @@ function Save-WebFile {
8382 # =================================================
8483 # OverWrite
8584 # =================================================
86- if ((-NOT ($PSBoundParameters [' Overwrite' ])) -and (Test-Path $DestinationFullName )) {
87- Write-Verbose " DestinationFullName already exists"
85+ if ((-not ($PSBoundParameters [' Overwrite' ])) -and (Test-Path $DestinationFullName )) {
86+ Write-Verbose ' DestinationFullName already exists'
8887 Get-Item $DestinationFullName - Force
8988 }
9089 else {
@@ -116,17 +115,57 @@ function Save-WebFile {
116115 else {
117116 Write-Verbose " cURL Source: $SourceUrl "
118117 Write-Verbose " Destination: $DestinationFullName "
119- $Headers = Invoke-Expression " & curl.exe --head --silent --insecure --location --url `" $SourceUrl `" "
120- if ($Headers [0 ] -match " 200.+OK" ) {
121- if ($host.name -match ' ConsoleHost' ) {
122- Invoke-Expression " & curl.exe --insecure --location --output `" $DestinationFullName `" --url `" $SourceUrl `" "
118+
119+ Write-Verbose ' Requesing HTTP HEAD to get Content-Length and Accept-Ranges header'
120+ $remote = Invoke-WebRequest - UseBasicParsing - Method Head - Uri $SourceUrl
121+ $remoteLength = $remote.Headers .' Content-Length'
122+ $remoteAcceptsRanges = ($remote.Headers .' Accept-Ranges' | Select-Object - First 1 ) -eq ' bytes'
123+
124+ $curlCommandExpression = " & curl.exe --insecure --location --output `" $DestinationFullName `" --url `" $SourceUrl `" "
125+
126+ if ($host.name -match ' PowerShell ISE Host' ) {
127+ # PowerShell ISE will display a NativeCommandError, so progress will not be displayed
128+ $Quiet = Invoke-Expression ($curlCommandExpression + ' 2>&1' )
129+ }
130+ else {
131+ Invoke-Expression $curlCommandExpression
132+ }
133+
134+ # =================================================
135+ # Continue interrupted download
136+ # =================================================
137+ if (Test-Path $DestinationFullName ) {
138+ $localExists = $true
139+ }
140+
141+ $RetryDelaySeconds = 1
142+ $MaxRetryCount = 10
143+ $RetryCount = 0
144+ while (
145+ $localExists `
146+ -and ((Get-Item $DestinationFullName ).Length -lt $remoteLength ) `
147+ -and $remoteAcceptsRanges `
148+ -and ($RetryCount -lt $MaxRetryCount )
149+ ) {
150+ Write-Verbose " Download is incomplete, remote server accepts ranges, will retry in $RetryDelaySeconds second(s)"
151+ Start-Sleep - Seconds $RetryDelaySeconds
152+ $RetryDelaySeconds *= 2 # retry with exponential backoff
153+ $RetryCount += 1
154+ $curlCommandExpression = " & curl.exe --insecure --location --continue-at - --output `" $DestinationFullName `" --url `" $SourceUrl `" "
155+
156+ if ($host.name -match ' PowerShell ISE Host' ) {
157+ # PowerShell ISE will display a NativeCommandError, so progress will not be displayed
158+ $Quiet = Invoke-Expression ($curlCommandExpression + ' 2>&1' )
123159 }
124160 else {
125- # PowerShell ISE will display a NativeCommandError, so progress will not be displayed
126- $Quiet = Invoke-Expression " & curl.exe --insecure --location --output `" $DestinationFullName `" --url `" $SourceUrl `" 2>&1"
161+ Invoke-Expression $curlCommandExpression
127162 }
128- } else {
129- Write-Warning " Header status: $ ( $headers [0 ]) "
163+ }
164+
165+ if ($localExists -and ((Get-Item $DestinationFullName ).Length -lt $remoteLength )) {
166+ Write-Verbose " Download is incomplete after $RetryCount retries."
167+ Write-Warning " Could not download $DestinationFullName "
168+ $null
130169 }
131170 }
132171 # =================================================
0 commit comments