繁体   English   中英

PowerShell:DownloadFileAsync的Runspace问题

[英]PowerShell: Runspace problem with DownloadFileAsync

我需要在PowerShell 2.0中使用WebClient下载文件,我想显示下载进度,所以我这样做:

$activity = "Downloading"

$client = New-Object System.Net.WebClient
$urlAsUri = New-Object System.Uri($url)

$event = New-Object System.Threading.ManualResetEvent($false)

$downloadProgress = [System.Net.DownloadProgressChangedEventHandler] {
    $progress = [int]((100.0 * $_.BytesReceived) / $_.TotalBytesToReceive)
    Write-Progress -Activity $activity -Status "${progress}% done" -PercentComplete $progress
}

$downloadComplete = [System.ComponentModel.AsyncCompletedEventHandler] {
    Write-Progress -Activity $activity -Completed
    $event.Set()
}

$client.add_DownloadFileCompleted($downloadComplete) 
$client.add_DownloadProgressChanged($downloadProgress)

Write-Progress -Activity $activity -Status "0% done" -PercentComplete 0
$client.DownloadFileAsync($urlAsUri, $file)    

$event.WaitOne()

我收到错误There is no Runspace available to run scripts in this thread. 对于$downloadProgress处理程序中的代码,这是合乎逻辑的。 但是,我怎么提供了一个Runspace的线程(可能)所属的ThreadPool

更新:请注意,这个问题的两个答案都值得一读,如果可以,我会同意接受。

谢谢你的点头。

powers,powershell有自己的线程池,每个服务线程都有一个指向运行空间的threadstatic指针(System.Management.Automation.Runspaces.Runspace.DefaultRunspace静态成员公开了这个 - 并且在你的回调中将是一个空引用。)最终这意味着很难 - 特别是在脚本中 - 使用你自己的线程池(由.NET提供异步方法)来执行脚本块。

PowerShell 2.0

无论如何,没有必要使用它,因为powershell v2完全支持事件:

$client = New-Object System.Net.WebClient
$url = [uri]"http://download.microsoft.com/download/6/2/F/" +
    "62F70029-A592-4158-BB51-E102812CBD4F/IE9-Windows7-x64-enu.exe"

try {

   Register-ObjectEvent $client DownloadProgressChanged -action {     

        Write-Progress -Activity "Downloading" -Status `
            ("{0} of {1}" -f $eventargs.BytesReceived, $eventargs.TotalBytesToReceive) `
            -PercentComplete $eventargs.ProgressPercentage    
    }

    Register-ObjectEvent $client DownloadFileCompleted -SourceIdentifier Finished

    $file = "c:\temp\ie9-beta.exe"
    $client.DownloadFileAsync($url, $file)

    # optionally wait, but you can break out and it will still write progress
    Wait-Event -SourceIdentifier Finished

} finally { 
    $client.dispose()
}

PowerShell v1.0

如果您坚持使用v1(这不是专门针对您的,因为您在问题中提到了v2),您可以在http://pseventing.codeplex.com/上使用我的powershell 1.0事件管理单元。

异步回调

.NET中另一个棘手的问题是异步回调。 在PowerShell的v1或v2中没有任何东西可以帮助你,但你可以通过一些简单的管道将异步回调转换为事件,然后使用常规事件处理该事件。 我在http://poshcode.org/1382上发布了一个脚本(New-ScriptBlockCallback)

希望这可以帮助,

-Oisin

我看到您使用异步调用,以便您可以显示进度。 然后你可以使用BitsTransfer模块。 它默认显示进度:

Import-Module BitsTransfer
Start-BitsTransfer -Source $url -dest d:\temp\yourfile.zip

如果您想在后台传输文件,可以使用以下内容:

Import-Module BitsTransfer

$timer = New-Object Timers.Timer
$timer.Interval = 300
Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action {
    if ($transfer.JobState -ne 'Transferring') { 
        $timer.Enabled = 0; 
        Write-Progress -Completed -Activity Downloading -Status done
        return 
    }
    $progress = [int](100* $transfer.BytesTransferred/$transfer.BytesTotal)
    Write-Progress -Activity Downloading -Status "$progress% done" -PercentComplete $progress
} -sourceId mytransfer
$transfer = Start-BitsTransfer -Source $url -dest d:\temp\yourfile.zip -async
$timer.Enabled = 1

# after that
Unregister-Event -SourceIdentifier mytransfer
$timer.Dispose()

关键参数是-async 它在后台开始传输。 我没有找到任何由传输触发的事件,因此我每秒查询作业以通过Timers.Timer对象报告状态。

但是,使用此解决方案,需要取消注册事件并配置计时器。 前段时间我在作为-Action传递的scriptblock中取消注册时遇到问题(它可能在if分支中),所以我在单独的命令中取消注册该事件。


我认为@oising(x0n)在他的博客上有一些解决方案。 他会尽希望地告诉你,那将是你问题的答案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM