PowerShell: Runspace problem with DownloadFileAsync

前端 未结 2 460

I needed to download file using WebClient in PowerShell 2.0, and I wanted to show download progress, so I did it this way:

$activity = \"Downloa         


        
相关标签:
2条回答
  • 2020-12-13 22:21

    Thanks stej for the nod.

    Andrey, powershell has its own threadpool and each service thread keeps a threadstatic pointer to a runspace (the System.Management.Automation.Runspaces.Runspace.DefaultRunspace static member exposes this - and would be a null ref in your callbacks.) Ultimately this means it's difficult - especially in script - to use your own threadpool (as is provided by .NET for async methods) to execute scriptblocks.

    PowerShell 2.0

    Regardless, there is no need to play with this as powershell v2 has full support for eventing:

    $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

    If you're stuck on v1 (this is not specifically for you as you mention v2 in the question) you can use my powershell 1.0 eventing snap-in at http://pseventing.codeplex.com/

    Async Callbacks

    Another tricky area in .NET is async callbacks. There is nothing directly in v1 or v2 of powershell that can help you here, but you can convert an async callback to an event with some simple plumbing and then deal with that event using regular eventing. I posted a script for this (New-ScriptBlockCallback) at http://poshcode.org/1382

    Hope this helps,

    -Oisin

    0 讨论(0)
  • 2020-12-13 22:21

    I see that you use async call so that you can show the progress. Then you can use BitsTransfer module for that. It shows the progress by default:

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

    If you would like to transfer the file in the background, you could use something like this:

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

    The key parameter is -async. It starts the transfer in background. I haven't found any event triggered by the transfer, so I query the job each second to report the state via Timers.Timer object.

    However with this solution, it is needed to unregister the event and dispose the timer. Some time ago I had problems with unregistering in the scriptblock passed as -Action (it could be in the if branch), so I unregister the event in separate command.


    I think @oising (x0n) has some solution on his blog. He'll tell you that hopefully and that would be answer for your question.

    0 讨论(0)
提交回复
热议问题