Home > Software engineering >  Sending email attachment - Converting CSV file to bytes removes line breaks
Sending email attachment - Converting CSV file to bytes removes line breaks

Time:01-27

I'm trying to use the MS Graph cmdlet Send-MgUserMail to send an email and include a CSV attachment.

If I do it like this, it works fine, and the attachment is fine.

Sample CSV attachment

"App","AppId","Date","User"
"App1","43a9087d-8551-47d5-9869-3287736ce7c3","2023/01/26","[email protected]"
"App2","f33750cd-c79b-43be-8e55-ee14d163e2b3","2024/01/26","[email protected]"

Working code

$attachment   = "C:\Users\Me\Downloads\SampleFile.csv"
$base64string = [Convert]::ToBase64String([IO.File]::ReadAllBytes($attachment))

$params = @{
    UserId = "[email protected]"
    BodyParameter = @{
        Message = @{
            Subject = "Test Email"
            Body    = @{
                ContentType = "Html"
                Content     = "This is sample text."
            }
            ToRecipients = @(
                @{
                    EmailAddress = @{
                        Address = "[email protected]"
                    }
                }
            )
            Attachments = @(
                @{
                    "@odata.type" = "#microsoft.graph.fileAttachment"
                    Name         = "SampleFile.csv"
                    ContentType  = "text/csv"
                    ContentBytes = $base64string
                }
            )
        }
        SaveToSentItems = "false"
    }
}
 
Send-MgUserMail @params

Objective

What I would like is to attach the CSV, without having an actual saved file. The content would be from memory, like so.

$output = @(
    [PSCustomObject] @{
        App = "App1"
        AppId = "43a9087d-8551-47d5-9869-3287736ce7c3"
        Date  = "2023/01/26"
        User  = "[email protected]"
    },
    [PSCustomObject] @{
        App = "App2"
        AppId = "f33750cd-c79b-43be-8e55-ee14d163e2b3"
        Date  = "2024/01/26"
        User  = "[email protected]"
    }
)

$attachment = $output | ConvertTo-Csv -NoTypeInformation

$bytes         = [System.Text.Encoding]::UTF8.GetBytes($attachment)
$base64string  = [Convert]::ToBase64String($bytes)

My issue when I do this is the CSV content is entirely on 1 line, there's no more line breaks.

Issue Sample CSV Output

"App","AppId","Date","User" "App1","43a9087d-8551-47d5-9869-3287736ce7c3","2023/01/26","[email protected]" "App2","f33750cd-c79b-43be-8e55-ee14d163e2b3","2024/01/26","[email protected]"

How can I convert this properly?

CodePudding user response:

Answer was given in comments but to give it closure, what happens is that ConvertTo-Csv outputs an array of strings and they don't have new line characters (CRLF in Windows, LF in Linux), before getting the bytes of your Csv you need to convert this array into a single multi-line string, for this you can use Out-String:

$attachment = $output | ConvertTo-Csv -NoTypeInformation | Out-String

Or join them by a new line character to preserve the structure:

$attachment = ($output | ConvertTo-Csv -NoTypeInformation) -join [Environment]::NewLine

Also, [System.Text.Encoding]::UTF8.GetBytes(...) which has a string type argument is coercing your array into a string and when you coerce an array into a string in PowerShell, it is joined by $OFS (a white space character by default) hence why you get a single line joined by whitespaces when decoding the base64 string.


Another way this could've been done is by assigning $OFS the value of a new line character:

$attachment = $output | ConvertTo-Csv -NoTypeInformation
$OFS = [System.Environment]::NewLine
$bytes         = [System.Text.Encoding]::UTF8.GetBytes($attachment)
$base64string  = [Convert]::ToBase64String($bytes)
$OFS = ' '

# Decoding should look as expected
[System.Text.Encoding]::UTF8.GetString(
    [Convert]::FromBase64String($base64string)
)
  • Related