Search code examples
powershellcopyzip

PowerShell - Copy zip file pass as string parameter to remote server


I am uploading zip file using .NET Core application and pass it as a string parameter to PowerShell script and trying to use following command for copy

Invoke-Command -Session $Session -ScriptBlock { Param($uplodedZipFileAsString,$DestinationFilePath) New-Item -Path $DestinationFilePath -Value $uplodedZipFileAsString -Force }  -ArgumentList ($uplodedZipFileAsString, $DestinationFilePath)

using above command file is copied at destination location, but while tried to unzip it. it gives an error as

The Compressed (zipped) Folder 'DestPath\uplodedZipFile.zip' is invalid.

Question :-

  1. How can copy zip file(passed as string parameter) to remote server?
  2. What are the best practices should follows in this scenario?

Note:- Assume that in above PowerShell command all parameters like $Session,$uplodedZipeFileAsString and $DestinationFolderFilePath are properly provided.

Edited:-

High level code details as follows

In .NET Core using file upload control we get IFormFile file(argument name) as parameter method looks as follows

public async Task<IActionResult> Index(IFormFile file)
{
   // following method convert FileStream To Sting 
    string fileString = ConvertFileStreamToString(file); // this is zip as string

   //following method execute powershell script 
   PowerShellHelper(string fileString,... other paramter)

  //Other Code...
}

private string ConvertFileStreamToString(IFormFile file)
{
  var fileString = new StringBuilder();
  using (var reader = new StreamReader(file.OpenReadStream()))
  {
     while (reader.Peek() >= 0)
        fileString.AppendLine(reader.ReadLine());
  }
  return fileString.ToString();
}

PowerShell as follows

 function zipcopyfunction
 {
   [CmdletBinding()]
   Param
   (
    [Parameter(Mandatory=$true, 
            Position=0,
            HelpMessage='Please Provide Zip File')]
   [ValidateNotNullOrEmpty()]
   [string]$uplodedZipFileAsString,
   #other necessary paramters goes here...
  )

 #Other code related to session creation
 try
 { 
   #other code including destination file path(DestinationFilePath) and $Session...
  $DestinationFilePath = "C:\test\destFile.zip"
  Invoke-Command -Session $Session -ScriptBlock { Param($uplodedZipFileAsString,$DestinationFilePath) New-Item -Path $DestinationFilePath -Value $uplodedZipFileAsString -Force }  -ArgumentList ($uplodedZipFileAsString, $DestinationFilePath)
  #remove session related code...
}
 catch
 {
      Throw $_.exception.message
  }
}

Solution

  • As commented, don't try to read a binary file as if it were a text file.

    Instead, read the file as an array of bytes ([Byte[]]) to get the actual file content unaltered. Then you could change your code to do this:

    # use the full, absolute filepaths as we're using .Net methods
    $sourceFile   = 'D:\somewhere\TheFile.zip'
    
    # the path (in this demo 'D:\Test') MUST exist locally in the remote machine
    $destination  = 'D:\Test\TheFile.zip' 
    
    # read the file as an array of Bytes 
    [byte[]]$bytes = [System.IO.File]::ReadAllBytes($sourceFile)
    
    Invoke-Command -Session $Session -ScriptBlock { 
        Param(
            [byte[]]$ZipBytes,
            [string]$DestinationFilePath
        ) 
        [System.IO.File]::WriteAllBytes($DestinationFilePath, $ZipBytes)
    } -ArgumentList @($bytes ,$destination)
    

    You could convert the binary data into a string, by converting the data to Base64 for instance using [Convert]::ToBase64String($bytes) but then in the scriptblock you will first need to convert that string back to bytes using [byte[]]$bytes = [Convert]::FromBase64String($uplodedZipFileAsString) and then write to file. This of course will use more processing time and the data you will be sending wil become much larger than the original number of bytes.