Search code examples
powershellrest

Powershell 7 fails when uploading a file with Invoke-RestMethod for multipart form but works in Powershell 5


I am uploading files to a resource using REST in the following way.

    #File names
    $PathToFile = "C:\Path to file here.zip"
    $PackageName = $(Split-Path $PathToFile -Leaf)

    #Byte stream and encoding
    $PackageByteStream = [System.IO.File]::ReadAllBytes("$PathToFile")
    $Encoding = [System.Text.Encoding]::GetEncoding("ISO-8859-1")
    $FileEncoding = $Encoding.GetString($PackageByteStream)

    #Create the content body
    $Boundary = (New-Guid).ToString()
    $ContentBody = "--$Boundary
Content-Disposition: form-data; name=""files""; filename=""$PackageName""
Content-Type: application/octet-stream

$FileEncoding
--$Boundary--
"

    #Parameter Object
    $Parameters = @{
        Uri                   = "http://myUrlhere.com/TargetResource"
        Method                = "Post"
        UseDefaultCredentials = $true
        ContentType           = "multipart/form-data; boundary=`"$Boundary`""
        Body                  = $ContentBody
    }
    if ($IsCoreCLR -and ($Parameters.Uri -match '^http:')) {
        $Parameters += @{ "AllowUnencryptedAuthentication" = $true }
    }

    #Upload the file
    Invoke-RestMethod @Parameters

This works in Powershell 5.1 but in Powershell 7 it fails with "Offset to Central Directory cannot be held in an Int64". The only related post I could find on that was someone who used ReadAllText instead of ReadAllBytes, but since I already use ReadAllBytes I don't think it's that.

Is there something that has changed between Powershell versions that would cause this behaviour?


Solution

  • I presume you've run into the following:

    • In PowerShell (Core) 7 v7.4+, the web cmdlets (Invoke-WebRequest, Invoke-RestMethod) consistently encode text-based request bodies as UTF-8, unless explicitly specified otherwise.

    • Previously, and still in Windows PowerShell, the default was ISO-8859-1, except for JSON, which has been UTF-8-encoded since v7.0.

    • To use ISO-8859-1 encoding in v7.4+, append ; charset=iso-8859-1 to the
      -ContentType value (or, via -Headers, to the Content-Type field).

    In other words, in the context of your splatted arguments:

    # ...
    
    $Parameters = @{
      Uri    = "http://myUrlhere.com/TargetResource"
      Method = "Post"
      UseDefaultCredentials = $true
      ContentType = "multipart/form-data; boundary=`"$Boundary`"; charset=iso-8859-1"
      Body = $ContentBody
    }
    
    # ...
    
    Invoke-RestMethod @Parameters
    

    Taking a step back:

    • In PowerShell 7, instead of manually constructing your multipart/form-data request body as text, consider using a streamlined approach based on a hashtable passed to the -Form parameter that references the file to upload via a Get-Item call: see example 6 of the Invoke-WebRequest help topic.

    • If you need more control over the submission, use the lower-level technique shown in example 5.

    • Note: These techniques should equally work with the Invoke-RestMethod cmdlet; Invoke-RestMethod and Invoke-WebRequest support the same parameters and differ only in the data type of their return value, reflecting their respective focus: Invoke-WebRequest is focused on HTML-page retrieval, whereas Invoke-RestMethod is focused on calling HTTP-based APIs (not only REST-ful ones); see this answer for more information.