Back to script library
Entra / Microsoft 365 · Exchange Online

Upload large attachment chunks

An example to show how to upload a large attachment in byte chunks for a draft message and then send the message.

Connect & set up

Run these once per session. All scopes are read-only unless the script makes changes.

Connect-MgGraph -Scopes "Mail.Send", "Mail.ReadWrite" -NoWelcome

Run it

The main script. Copy it, or download the .ps1 and run it from your console.

Connect-MgGraph -Scopes "Mail.Send", "Mail.ReadWrite" -NoWelcome
# Custiomize this address!
$RecipientAddress = 'Hans.Geering@Office365ITPros.com'
# Customize this path to a large file that exists on your system
$AttachmentFile = "C:\Temp\PowerShellBook.pdf"
[int32]$MaxAttachmentSize = 146800640
$FileStream = New-Object System.IO.StreamReader($AttachmentFile)
$FileSize = $FileStream.BaseStream.Length
# The maxiumum size of an attachment is 150 MB (157286400 bytes). There's some overhead, so we'll restrict this to 140 MB (146800640 bytes).
If ($FileSize -gt $MaxAttachmentSize) {
Write-Host ("Attachment {0} is too large ({1} bytes). Maximum size is {2} bytes (140 MB)" -f $AttachmentFile, $FileSize, $MaxAttachmentSize)
Break
}
# Build structure for the attachment
$AttachmentDetail = @{}
$AttachmentDetail.Add("attachmentType", "file")
$AttachmentDetail.Add("name", [System.IO.Path]::GetFileName($AttachmentFile))
$AttachmentDetail.Add("size", $FileSize)
$AttachmentParams = @{}
$AttachmentParams.Add("AttachmentItem", $AttachmentDetail)
# Create message structure
$HtmlBody = "<b>Isn't it nice to get a large attachment?<b>"
$MsgSubject = "A very large attachment for you to read"
$MsgFrom = (Get-MgContext).Account
$MsgParams = @{}
$MsgParams.Add("Content", $HtmlBody)
$MsgParams.Add("ContentType", "html")
$EmailAddress = @{address = $RecipientAddress}
$EmailRecipient = @{EmailAddress = $EmailAddress}
Write-Host "Sending welcome email to Hans Geering"
# Create a draft message in the signed-in user's mailbox
$NewMessage = New-MgUserMessage -UserId $MsgFrom -Body $MsgParams -ToRecipients $EmailRecipient -Subject $MsgSubject
# Create an upload session
$UploadSession = New-MgUserMessageAttachmentUploadSession -UserId $MsgFrom -MessageId $NewMessage.Id -BodyParameter $AttachmentParams
# Define chunk size for uploading attachments. Must be a multiple of 320 KB ( 327680 bytes)
[Int32]$uploadChunkSize = 983040
# Upload the attachment file in chunks
$FileOffsetStart = 0
$FileBuffer = [byte[]]::new($uploadChunkSize)
do {
$FileChunkByteCount = $FileStream.BaseStream.Read($FileBuffer, 0, $FileBuffer.Length)
Write-Verbose ($FileStream.BaseStream.Position)
$FileOffsetEnd = $FileStream.BaseStream.Position - 1
if ($FileChunkByteCount -gt 0) {
$UploadRangeHeader = "bytes " + $FileOffsetStart + "-" + $FileOffsetEnd + "/" + $FileSize
Write-Verbose $UploadRangeHeader
$FileOffsetStart = $fileStream.BaseStream.Position
$BinaryContent = New-Object System.Net.Http.ByteArrayContent -ArgumentList @($FileBuffer, 0, $FileChunkByteCount)
$FileBuffer = [byte[]]::new($uploadChunkSize)
$Headers = @{
'AnchorMailbox' = $MsgFrom
'Content-Range' = $UploadRangeHeader
}
$Result = (Invoke-RestMethod -Method Put -Uri $UploadSession.UploadUrl -UserAgent "UploadAgent" -Headers $Headers -Body $BinaryContent.ReadAsByteArrayAsync().Result -ContentType "application/octet-stream")
Write-Verbose $Result
}
} while ($FileChunkByteCount -ne 0)
# Send the message
Try {
Send-MgUserMessage -UserId $MsgFrom -MessageId $NewMessage.Id -ErrorAction Stop
Write-Host "Message sent to $RecipientAddress"
}
Catch {
Write-Host "Error sending message: $_"
}
Attribution