【问题标题】:Is there any way to monitor the progress of a download using a WebClient object in powershell?有没有办法在 powershell 中使用 WebClient 对象监控下载进度?
【发布时间】:2014-02-20 17:47:44
【问题描述】:

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

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

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

【问题讨论】:

  • 如果可以升级到v3,可以使用内置命令Invoke-WebRequest https://www.site.com/file -OutFile C:/Local/Path/file,它会自动显示进度。

标签: powershell powershell-2.0 webclient webclient-download


【解决方案1】:

要显示下载文件的进度条,请查看 Jason Niver 的博客文章:

Downloading files from the internet in Power Shell (with progress)

基本上,您可以创建一个仍使用 Web 客户端功能但包含捕获状态的方法的函数。然后,您可以使用 写入进度 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:\temp\largefile.zip"

此外,还有一些来自 docs.microsoft 的其他 Write-Progress 示例,用于 powrshell 7。

Write-Progress

【讨论】:

  • 我知道write-progress;但是,我认为没有任何方法可以增加这样的计数器来显示下载进度。
  • 这个函数比直接调用下载要慢得多。但是,如果您将缓冲区大小更改为 1000KB,则会产生巨大的差异。使用 10KB 的缓冲区,下载一个 300MB 的文件需要 392 秒。在 1000KB 时,只用了 43 秒。理想大小可能因文件大小和连接速度而异,但对于大多数下载而言,10KB 可能太小了。
  • 您的 Write-Progress 链接不再有效
  • @Gwasshoppa 谢谢!更新到 Microsoft 链接。 docs.microsoft.com/en-us/powershell/module/…
  • Jason Niver 博客链接损坏
【解决方案2】:

在 V2 中,您可以只使用 BitsTransfer 模块,例如:

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

【讨论】:

  • $urls | % { $name = ([uri]$_).Segments[-1]; Start-BitsTransfer $_ -DisplayName $name }
  • 请记住,BITS 只使用空闲的网络带宽。
  • 请注意,如果您想在计划任务中使用脚本,这不起作用
  • 如果在计划任务中运行,为什么这不起作用?如果你确定,你找到方法检查了吗?如果手动运行将使用比特传输运行的代码,但如果通过计划任务运行则使用另一种方法?
【解决方案3】:

在我重写一些安装脚本时,我遇到了这个问题。这些方法都不是理想的。第一种方法在 Windows PowerShell 上存在性能问题,第二种方法需要最新的系统,我们都知道它在现实生活中是如何工作的。 ??‍♀️

为了获得最佳性能,我将这两种方法结合到一个智能方法中,使其更加通用,遵循 PowerShell 最佳实践。

可以在here找到一些基准。

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()
    }
}

【讨论】:

    猜你喜欢
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    • 2021-02-27
    • 1970-01-01
    • 2018-04-09
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    相关资源
    最近更新 更多