我正在使用这样一个简单的命令行下载文件:
$webclient = New-Object -TypeName System.Net.WebClient
$webclient.DownloadFile("https://www.example.com/file", "C:/Local/Path/file")
问题是我想在下载过程中使用弹出窗口向用户显示消息,或者在shell中使用进度条。是否可能制作一个弹出框,在下载完成后自动消失,或者制作一个可以监视下载进度的进度条?
我正在使用这样一个简单的命令行下载文件:
$webclient = New-Object -TypeName System.Net.WebClient
$webclient.DownloadFile("https://www.example.com/file", "C:/Local/Path/file")
问题是我想在下载过程中使用弹出窗口向用户显示消息,或者在shell中使用进度条。是否可能制作一个弹出框,在下载完成后自动消失,或者制作一个可以监视下载进度的进度条?
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()
}
downloadFile "http://example.com/largefile.zip" "c:\temp\largefile.zip"
write-progress
的使用方法;但是,我认为没有办法像那样递增一个计数器以显示下载进度。 - EGrImport-Module BitsTransfer
Start-BitsTransfer https://www.example.com/file C:/Local/Path/file
$urls | % { $name = ([uri]$_).Segments[-1]; Start-BitsTransfer $_ -DisplayName $name }
- Markus Jarderot当我在重写一些安装脚本时,我遇到了这个问题。这些方法都不是理想的方法。第一种方法在Windows PowerShell上存在性能问题,第二种方法需要系统更新,我们都知道在现实生活中它是如何工作的。
为了实现最佳性能,我将两种方法结合成了一个更智能、更通用的解决方案,遵循PowerShell最佳实践。
一些基准测试结果可以在这里找到。
function Get-File
{
param (
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[System.Uri]
$Uri,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[System.IO.FileInfo]
$TargetFile,
[Parameter(ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[Int32]
$BufferSize = 1,
[Parameter(ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[ValidateSet('KB, MB')]
[String]
$BufferUnit = 'MB',
[Parameter(ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[ValidateSet('KB, MB')]
[Int32]
$Timeout = 10000
)
$useBitTransfer = $null -ne (Get-Module -Name BitsTransfer -ListAvailable) -and ($PSVersionTable.PSVersion.Major -le 5)
if ($useBitTransfer)
{
Write-Information -MessageData 'Using a fallback BitTransfer method since you are running Windows PowerShell'
Start-BitsTransfer -Source $Uri -Destination "$($TargetFile.FullName)"
}
else
{
$request = [System.Net.HttpWebRequest]::Create($Uri)
$request.set_Timeout($Timeout) #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.FullName)", Create
switch ($BufferUnit)
{
'KB' { $BufferSize = $BufferSize * 1024 }
'MB' { $BufferSize = $BufferSize * 1024 * 1024 }
Default { $BufferSize = 1024 * 1024 }
}
Write-Verbose -Message "Buffer size: $BufferSize B ($($BufferSize/("1$BufferUnit")) $BufferUnit)"
$buffer = New-Object byte[] $BufferSize
$count = $responseStream.Read($buffer, 0, $buffer.length)
$downloadedBytes = $count
$downloadedFileName = $Uri -split '/' | Select-Object -Last 1
while ($count -gt 0)
{
$targetStream.Write($buffer, 0, $count)
$count = $responseStream.Read($buffer, 0, $buffer.length)
$downloadedBytes = $downloadedBytes + $count
Write-Progress -Activity "Downloading file '$downloadedFileName'" -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 '$downloadedFileName'"
$targetStream.Flush()
$targetStream.Close()
$targetStream.Dispose()
$responseStream.Dispose()
}
}
这是一个老问题,但我有一个优雅的解决方案。
$webClient = New-Object -TypeName System.Net.WebClient
$webClient.Credentials = $login
$task = $webClient.DownloadFileTaskAsync($uri, $PSScriptRoot + "\" + $asset)
Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -SourceIdentifier WebClient.DownloadProgressChanged | Out-Null
Start-Sleep -Seconds 3
while (!($task.IsCompleted)) {
$EventData = Get-Event -SourceIdentifier WebClient.DownloadProgressChanged | Select-Object -ExpandProperty "SourceEventArgs" -Last 1
$ReceivedData = ($EventData | Select-Object -ExpandProperty "BytesReceived")
$TotalToReceive = ($EventData | Select-Object -ExpandProperty "TotalBytesToReceive")
$TotalPercent = $EventData | Select-Object -ExpandProperty "ProgressPercentage"
Start-Sleep -Seconds 2
Write-Progress -Activity "Downloading File" -Status "Percent Complete: $($TotalPercent)%" -CurrentOperation "Downloaded $(convertFileSize -bytes $ReceivedData) / $(convertFileSize -bytes $TotalToReceive)" -PercentComplete $TotalPercent
}
Unregister-Event -SourceIdentifier WebClient.DownloadProgressChanged
$webClient.Dispose()
现在,您需要另一个用于转换文件大小的函数:
function convertFileSize {
param(
$bytes
)
if ($bytes -lt 1MB) {
return "$([Math]::Round($bytes / 1KB, 2)) KB"
}
elseif ($bytes -lt 1GB) {
return "$([Math]::Round($bytes / 1MB, 2)) MB"
}
elseif ($bytes -lt 1TB) {
return "$([Math]::Round($bytes / 1GB, 2)) GB"
}
}
我从Invoke-DownloadFile中提取了代码片段。感谢Tim Small。
你也可以使用它来获得更快的下载速度,以及一个令人印象深刻的进度条。
$fullurl='here put URL of the file which one you want to download'
$BASE_DIR= 'specify the path of the file to be stored '
$useBitTransfer = $null -ne (Get-Module -Name BitsTransfer -ListAvailable) -and ($PSVersionTable.PSVersion.Major -le 5)
if ($useBitTransfer)
{
Write-Information -MessageData 'Using a fallback BitTransfer method since you are running Windows PowerShell'
Start-BitsTransfer -Source $fullUrl -Destination $BASE_DIR\kubescape.exe
}
else
{
Invoke-WebRequest -Uri $fullUrl -OutFile $BASE_DIR\kubescape.exe
}
Invoke-WebRequest https://www.site.com/file -OutFile C:/Local/Path/file
,它将自动显示进度。 - Keith Hill