2011-02-07 82 views
11

我需要下載使用WebClient文件中的PowerShell 2.0,我想展示下載進度,所以我也這樣說:PowerShell的:運行空間問題DownloadFileAsync

$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處理程序中,這是合乎邏輯的。但是,如何爲(可能)屬於ThreadPool的線程提供Runspace

UPDATE: 注意這個問題的答案是值得一讀的,如果我能我會接受這兩種。

回答

12

謝謝stej的點頭。

安德烈,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 1.0

如果您'重新粘在v1上(這個問題並不是專門針對你的,因爲你在問題中提到了v2),你可以使用我的powershell 1.0事件管理單元http://pseventing.codeplex.com/

異步回調

在.NET另一個棘手的面積是異步回調。powershell的v1或v2中沒有任何東西可以幫助你,但是你可以用一些簡單的管道將異步回調轉換爲事件,然後使用常規事件處理該事件。我在http://poshcode.org/1382

希望這有助於發佈這個(新ScriptBlockCallback)的腳本,

-Oisin

6

我看到您使用異步調用,以便您可以顯示進度。然後你可以使用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(它可能在if分支中)傳遞的scriptblock中發生了取消註冊的問題,因此我使用單獨的命令取消了註冊事件。


我覺得@oising(x0n)在他的博客上有一些解決方案。他會很有希望地告訴你,那將是你的問題的答案。

+0

謝謝,這是純粹的天才。比我的解決方案好得多。現在我會稍微等一下,看看有沒有人在接受之前回答Runspace部分(只是好奇它是如何處理的)。 – 2011-02-07 21:08:52

+0

我會爲另一種選擇提供靈感:) – stej 2011-02-07 21:30:25