是否有任何方法可以使用PowerShell中的WebClient对象监视下载的进度



我正在使用这样的简单行下载文件:

$webclient = New-Object -TypeName System.Net.WebClient
$webclient.DownloadFile("https://www.example.com/file", "C:/Local/Path/file")

问题在于,我想在使用弹出窗口下载或使用Shell中的进度栏下载时向用户显示一条消息。是否可以制作下载完成后消失的弹出框,或者可以监视下载进度的进度栏?

显示一个用于下载文件的进度栏查看Jason Niver的博客文章:

从电源外壳下载文件(随着进度)

基本上,您可以创建一个仍然使用Web客户端功能但包含捕获状态的方法。然后,您可以使用Write-Progress Power 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:templargefile.zip"

另外,以下是docs.microsoft的其他一些写入示例。

写入 -

在v2中您只需使用bitstransfer模块,例如:

Import-Module BitsTransfer
Start-BitsTransfer https://www.example.com/file C:/Local/Path/file

我在重写一些安装脚本时,遇到了这个问题。这些方法都不是理想的。第一种方法在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中获取摘要。蒂姆·斯莫尔的学分。

您也可以将其用于更好的下载速度,也可以用于令人印象深刻的进度栏。

$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_DIRkubescape.exe
        
    }
    else
    {
       Invoke-WebRequest -Uri $fullUrl -OutFile $BASE_DIRkubescape.exe
    }

最新更新