【问题标题】:Progress bar in webclientwebclient中的进度条
【发布时间】:2017-05-31 21:46:47
【问题描述】:

所以,我有一个显示从 FTP 下载进度的脚本。 我只是尝试了很多方法来解决这个任务。 其中一个结论是 cmdlet Register-ObjectEvent 是一个非常糟糕的主意。Powershell 对异步事件的支持相当差...
然后我停在那里 -

$webclient.add_DownloadProgressChanged([System.Net.DownloadProgressChangedEventHandler]$webclient_DownloadProgressChanged )
....
$webclient_DownloadProgressChanged = {
param([System.Net.DownloadProgressChangedEventArgs]$Global:e)
$progressbaroverlay1.value=$e.ProgressPercentage
....
}


此脚本中的所有内容都运行良好,但您可以理解我这样做是为了一个文件。
但后来我开始思考 - 如何同时下载多个文件并将其显示在一个进度条中? 那么有人有什么好主意吗?还是解决此任务的最佳方法?

附言

WebClient 一次只能下载一个文件。

当然,我知道。

【问题讨论】:

  • 两个进度条,第一个显示y个文件中的x个,第二个显示第二个文件的进度?否则,您必须找到每个文件的大小,跟踪下载的总字节数,并对其进行数学运算以完成您的百分比。
  • 所以,制作多个网络客户端。有一个脚本范围变量,它们都随着各自的进度而更新,并让进度条显示(他们的进度总和/有多少)..
  • @TessellatingHeckler 可以,但不能同时运行两个或多个 $WebClient.DownloadFileAsync。
  • @DaniilKuzmin 我制作了两个 WebClient 对象,将它们都设置为异步下载,它对我有用;看看 Process Monitor 如何跟踪同时写入的两个 ISO 文件,这两个文件都下载了数百 Mb(偏移量),没有排队或失败? i.imgur.com/N82WoEO.png
  • @TessellatingHeckler 没那么简单)..当您使用 EventHandler 时,游戏即将发生变化。现在你可以尝试在没有 write-progress 和 Register-ObjectEvent 的情况下显示 $e.ProgressPercentage。

标签: powershell handler webclient


【解决方案1】:

我想出了相同类型的 Scriptblock Create 方法,但我确实使用了 Register-ObjectEvent,或多或少取得了成功。异步下载作为后台作业发生,并使用事件将其进度传达回主脚本。

$Progress = @{}

$Isos = 'https://cdimage.debian.org/debian-cd/current/i386/iso-cd/debian-8.8.0-i386-CD-1.iso', 
        'https://cdimage.debian.org/debian-cd/current/i386/iso-cd/debian-8.8.0-i386-CD-2.iso'

$Count = 0
$WebClients = $Isos | ForEach-Object {

    $w = New-Object System.Net.WebClient

    $null = Register-ObjectEvent -InputObject $w -EventName DownloadProgressChanged -Action ([scriptblock]::Create(
        "`$Percent = 100 * `$eventargs.BytesReceived / `$eventargs.TotalBytesToReceive; `$null = New-Event -SourceIdentifier MyDownloadUpdate -MessageData @($count,`$Percent)"
    ))
    $w.DownloadFileAsync($_, "C:\PATH_TO_DOWNLOAD_FOLDER\$count.iso")

    $Count = $Count + 1
    $w
}

$event = Register-EngineEvent -SourceIdentifier MyDownloadUpdate -Action {
    $progress[$event.MessageData[0]] = $event.MessageData[1]
}

$Timer = New-Object System.Timers.Timer
Register-ObjectEvent -InputObject $Timer -EventName Elapsed -Action { 
    if ($Progress.Values.Count -gt 0) 
    {
        $PercentComplete =  100 * ($Progress.values | Measure-Object -Sum | Select-Object -ExpandProperty Sum) / $Progress.Values.Count
        Write-Progress -Activity "Download Progress" -PercentComplete $PercentComplete
    }
}

$timer.Interval = 100
$timer.AutoReset = $true
$timer.Start()

为读者练习如何判断下载已完成。

【讨论】:

【解决方案2】:

您可以使用 BitsTransfer 模块异步下载。 https://technet.microsoft.com/en-us/library/dd819420.aspx

显示 3 个文件的整体过程的示例代码,您指定相等的 url 数组和下载位置,您可以根据自己的喜好进一步处理,例如异常处理等:

Import-Module BitsTransfer
[string[]]$url = @();
$url += 'https://www.samba.org/ftp/talloc/talloc-2.1.6.tar.gz';
$url += 'https://e.thumbs.redditmedia.com/pF525auqxnTG-FFj.png';
$url += 'http://bchavez.bitarmory.com/Skins/DayDreaming/images/bg-header.gif';

[string[]]$destination = @();
$destination += 'C:\Downloads\talloc-2.1.6.tar.gz';
$destination += 'C:\Downloads\pF525auqxnTG-FFj.png';
$destination += 'C:\Downloads\bg-header.gif';

$result = Start-BitsTransfer -Source $url -Destination $destination -TransferType Download -Asynchronous
$downloadsFinished = $false;
    While ($downloadsFinished -ne $true) {
        sleep 1
        $jobstate = $result.JobState;
        if($jobstate.ToString() -eq 'Transferred') { $downloadsFinished = $true }
        $percentComplete = ($result.BytesTransferred / $result.BytesTotal) * 100
        Write-Progress -Activity ('Downloading' + $result.FilesTotal + ' files') -PercentComplete $percentComplete 
}

【讨论】:

  • 据我所知,目前不支持 FTP。
【解决方案3】:

我看到了两个可能的概念:

  1. 动态创建(使用[scriptblock]::Create)匿名函数,类似于:

$Id = 0
... | ForEach {
    $webclient[$Id].add_DownloadProgressChanged([System.Net.DownloadProgressChangedEventHandler]{[scriptblock]::Create("
        ....
        `$webclient_DownloadProgressChanged = {
        param([System.Net.DownloadProgressChangedEventArgs]`$e)
        `$Global:ProgressPercentage[$Id]=`$e.ProgressPercentage
        `$progressbaroverlay1.value=(`$Global:ProgressPercentage | Measure-Object -Average).Average
        ....
        "
        $Id++
    })
}

请注意,在这个想法中,您需要防止除 $Id 之外的所有内容都直接用反引号解释。

或者如果函数太大而无法读取,请简化[ScriptBlock]

[ScriptBlock]::Create("param(`$e); webclient_DownloadProgressChanged $Id `$e")

并调用一个全局函数:

$Global:webclient_DownloadProgressChanged($Id, $e) {
    $Global:ProgressPercentage[$Id]=$e.ProgressPercentage
    $progressbaroverlay1.value=($Global:ProgressPercentage | Measure-Object -Average).Average
}
  1. 创建您自己的自定义后台工作者(线程):

示例见:PowerShell: Job Event Action with Form not executed

  • 在主线程中构建带有进度条的 UI
  • 对于每个 FTP 下载:
    • 创建共享(隐藏)窗口控件(例如.TextBox[$Id]
    • 启动一个新的后台工作人员并共享相关控件,例如:
      $SyncHash = [hashtable]::Synchronized(@{TextBox = $TextBox[$Id]})
  • 从 WebWorker(s) 中更新共享的 $SyncHash.TextBox.Text =
  • 在每个.TextBox[$Id] 中捕获事件(例如.Add_TextChanged) 主线程
  • 根据每个.TextBox[$Id].Text 中传递的平均状态相应地更新您的进度条

【讨论】:

  • 这是一个“简单”、强大的答案。我会试试看!大 tnx!
  • 为什么?我更愿意将其保留在此处...但如果您认为有必要,我更愿意与您联系(让我知道如何)。
  • 我已连接到您的 LinkedIn 帐户
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-13
相关资源
最近更新 更多