Murasaki
Murasaki

Reputation: 119

Copy file during remote powershell session c# runspace

I need to copy a file from a location on an Azure VM to my local computer using powershell. e.g C:\tmp on the VM to C:\tmp on my local machine

The application is c# using System Automation. Currently I use the method.

    using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
                    {
                        runspace.Open();
                        Pipeline pipeline = runspace.CreatePipeline(//Some SQL that pulls files from database to C:\tmp);
                        var results = pipeline.Invoke();
    }

The powershell I use at the moment just return get-content for each file, returns it to c# in a loop, writing to file each time. However this is very inefficient.

Upvotes: 2

Views: 810

Answers (1)

theadriangreen
theadriangreen

Reputation: 2258

This is the right idea, however it is more efficient to chunk the file when you do this. Powershell does not currently have a native way to do this, so you have to write some code. There are two parts, the remote powershell part to chunk the file on the server, and the C# part to re-assemble the chunks and execute the powershell.

The remote powershell part:

$streamChunks = New-Object System.Collections.Generic.List[byte[]]
$buffer = New-Object byte[] 1024
[IO.FileStream] $fileStream = $null
try  
{  
    $targetPath = # FILE TO GET
    $fileStream = [IO.File]::OpenRead($targetPath)
    [int] $bytesRead = 0
    while (($bytesRead = $fileStream.Read($buffer, 0, 1024)) -gt 0)  
    {  
        $chunk = New-Object byte[] $bytesRead
        [Array]::Copy($buffer, $chunk, $bytesRead)
        $streamChunks.Add($chunk)
    }  
    Write-Output $streamChunks
}  
finally  
{   
    if ($fileStream -ne $null)   
    {  
        $fileStream.Dispose()  
        $fileStream = $null 
    }
};

Note that this script will be invoked by a runspace on your local machine:

Pipeline pipeline = runspace.CreatePipeline(command); // command is powershell above
Collection<PSObject> results = pipeline.Invoke();

The C# part to re-assemble the chunks:

using (FileStream fileStream = new FileStream(localPath, FileMode.OpenOrCreate, FileAccess.Write))
{
    foreach (PSObject result in results)
    {
        if (result != null)
        {
            byte[] chunk = (byte[])result.BaseObject;
            if (chunk != null)
            {
                fileStream.Write(chunk, 0, chunk.Length);
            }
        }
    }
}

Upvotes: 1

Related Questions