You’re focusing on the wrong layer.
What you see in Wireshark (headers in one TCP packet, body in another) is just TCP segmentation. From the HTTP point of view this is still a single request, and HTTP/1.1 explicitly allows the body to arrive in multiple segments. A server that only works when headers and body are in the same TCP packet is simply broken.
Because of that, there’s no supported way in .NET Framework to “force” HttpClient to put headers and body into one packet; the OS and network stack are free to split the data however they want.
What is different in your failing trace is this part of the headers:
Expect: 100-continue
Connection: Keep-Alive
With Expect: 100-continue, .NET sends the headers first, waits for a 100 Continue from the server, and only then sends the body. Your embedded device is sending a 400 instead of 100, so it’s not handling this handshake correctly.
You’ve already tried ServicePointManager.Expect100Continue = false, but with HttpClient on .NET Framework you should make sure this is set before creating the client, and also disable it on the request headers:
ServicePointManager.Expect100Continue = false;
var handler = new HttpClientHandler();
var client = new HttpClient(handler);
client.DefaultRequestHeaders.ExpectContinue = false;
using var payload = new MultipartFormDataContent();
payload.Add(new StreamContent(File.OpenRead(filename)), "file", Path.GetFileName(filename));
var response = await client.PostAsync(requestUrl, payload);
If the device still rejects the request after removing Expect: 100-continue, then the issue isn’t packet splitting. it’s simply that the device’s HTTP implementation doesn’t fully conform to HTTP/1.1. In that case your only real options are:
Fix/update the firmware / server on the device, or
Bypass HttpClient and use a raw Socket to manually send bytes that reproduce the exact request that you know works (like the curl request), but that’s a fragile workaround.
So, in short:
Is there any way to make .NET Framework 4.8 HttpClient send the file data directly in the initial request?
No, not reliably. You can (and should) disable Expect: 100-continue, but you can’t control how the HTTP request is split into TCP packets; the embedded server needs to handle segmented requests correctly.