
Editorial illustration.
Originally published 23 June 2023; rewritten and reviewed on 15 October 2026.
The requirement sounds simple: send a file to another service. The correct implementation depends on what that service accepts and what happens when the connection fails halfway through. I would choose the protocol before choosing a convenience class.
Match the method to the service
| Approach | When it fits |
|---|---|
| Multipart HTTP upload | A form-style API accepting files and related fields |
| Raw binary request body | An endpoint expecting only the file bytes |
| Presigned object-storage upload | A server grants narrowly scoped temporary storage access |
| Chunked or resumable protocol | A supported API needs recovery for large transfers |
| Vendor SDK | The service provides a maintained client for its upload contract |
These are five transport patterns, not interchangeable requests to the same endpoint. The receiving service determines which one is valid.
Stream a multipart body
A modern C# fragment using an existing, appropriately configured HttpClient illustrates the common case:
await using var stream = File.OpenRead(filePath);
using var form = new MultipartFormDataContent();
form.Add(new StreamContent(stream), "file", "upload.bin");
using var response = await httpClient.PostAsync(
uploadUri, form, cancellationToken);
response.EnsureSuccessStatusCode();
The caller must supply a trusted file path, endpoint and authentication. Stream the file rather than first turning a large file into an equally large byte array.
Define retry and acceptance
A successful HTTP status should be interpreted according to the provider’s response contract. It may mean the file was accepted for later processing. Retrying a completed request can create duplicates unless the API provides an idempotency or upload-session mechanism.
On the receiving side, enforce permissions, size and format checks independently. A client-side extension check is not validation for the server. Test cancellation and partial transfer as well as a small successful upload; those cases determine whether the integration can recover safely.