How to send a file with HttpClient in C#

By FoxLearn Published on Mar 11, 2025   176
When uploading a file with HttpClient, you need to place the file inside a MultipartFormDataContent object, which will be sent as the body of the HTTP request.

For example, How to upload a PDF file:

var filePath = @"C:\docs\report.pdf";

using (var multipartFormContent = new MultipartFormDataContent())
{
    // Load the PDF file and set its Content-Type header
    var fileStreamContent = new StreamContent(File.OpenRead(filePath));
    fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");

    // Add the file to the form data
    multipartFormContent.Add(fileStreamContent, name: "document", fileName: "report.pdf");

    // Send the POST request
    var response = await httpClient.PostAsync("https://example.com/upload", multipartFormContent);
    response.EnsureSuccessStatusCode();

    // Return the response body as a string
    return await response.Content.ReadAsStringAsync();
}

This will send the following multipart/form-data POST request:

POST https://example.com/upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary="f7c6bc18-ec75-4f78-bcfd-5b48f5645fc5"
Content-Length: 4567

--f7c6bc18-ec75-4f78-bcfd-5b48f5645fc5
Content-Type: application/pdf
Content-Disposition: form-data; name=document; filename=report.pdf; filename*=utf-8''report.pdf

<bytes>

In this example:

  • MultipartFormDataContent: This object is used to store the file content and any other fields that need to be sent as part of the request body.
  • File Content: The file is read using a StreamContent object, and the file's MIME type (application/pdf) is set explicitly.
  • Sending the Request: httpClient.PostAsync() sends the request to the server, where the file is uploaded.

Sending Additional Fields Along with the File

You can also send extra form data along with the file.

For instance, let's say you want to send the title of the document and the user ID.

var filePath = @"C:\docs\report.pdf";

using (var multipartFormContent = new MultipartFormDataContent())
{
    // Add extra fields
    multipartFormContent.Add(new StringContent("987"), name: "UserId");
    multipartFormContent.Add(new StringContent("Quarterly report"), name: "Title");

    // Add the PDF file
    var fileStreamContent = new StreamContent(File.OpenRead(filePath));
    fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
    multipartFormContent.Add(fileStreamContent, name: "document", fileName: "report.pdf");

    // Send the request
    var response = await httpClient.PostAsync("https://example.com/upload", multipartFormContent);
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsStringAsync();
}

This would send the following request with the extra fields:

POST https://example.com/upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary="3e9b4e5d-d66a-4b06-bc6f-c2a7b32a32f3"
Content-Length: 4897

--3e9b4e5d-d66a-4b06-bc6f-c2a7b32a32f3
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=UserId

987
--3e9b4e5d-d66a-4b06-bc6f-c2a7b32a32f3
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=Title

Quarterly report
--3e9b4e5d-d66a-4b06-bc6f-c2a7b32a32f3
Content-Type: application/pdf
Content-Disposition: form-data; name=document; filename=report.pdf; filename*=utf-8''report.pdf

<bytes>

Sending Multiple Files

If you need to send multiple files in the same request, you can use a similar approach.

For example, How to send multiple PDF files:

var filePaths = new string[] { @"C:\docs\report1.pdf", @"C:\docs\report2.pdf" };

using (var multipartFormContent = new MultipartFormDataContent())
{
    foreach (var filePath in filePaths)
    {
        var fileName = Path.GetFileName(filePath);

        // Load the file and set its Content-Type header
        var fileStreamContent = new StreamContent(File.OpenRead(filePath));
        fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");

        // Add each file
        multipartFormContent.Add(fileStreamContent, name: "documents", fileName: fileName);
    }

    // Send the request
    var response = await httpClient.PostAsync("https://example.com/upload", multipartFormContent);
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsStringAsync();
}

This sends the following request with multiple files:

POST https://example.com/upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary="bc85c0f7-bf9f-46a1-a876-25f7c6d9f3f4"
Content-Length: 10245

--bc85c0f7-bf9f-46a1-a876-25f7c6d9f3f4
Content-Type: application/pdf
Content-Disposition: form-data; name=documents; filename=report1.pdf; filename*=utf-8''report1.pdf

<bytes>

--bc85c0f7-bf9f-46a1-a876-25f7c6d9f3f4
Content-Type: application/pdf
Content-Disposition: form-data; name=documents; filename=report2.pdf; filename*=utf-8''report2.pdf

<bytes>

Setting the File's Content Type (PDF File Example)

The file "report.pdf" has a content type of "application/pdf", which needs to be explicitly set when sending the file.

// Cached MIME type mappings
var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
    [".pdf"] = "application/pdf",
    [".docx"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    [".txt"] = "text/plain"
};

var filePath = @"C:\docs\report.pdf";

// Extract the file extension
var extension = Path.GetExtension(filePath);

if (!map.TryGetValue(extension, out string contentType))
{
    throw new Exception("Can't send this type of file");
}

// Read the file as a stream
var fileStreamContent = new StreamContent(File.OpenRead(filePath));
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);

This sets the Content-Type header to application/pdf for the file in the multipart request, like this:

--b60a0be2-f5a8-4723-a566-10fbd4a20d74
Content-Type: application/pdf
Content-Disposition: form-data; name=document; filename=report.pdf; filename*=utf-8''report.pdf

In this example:

  • MIME Type Dictionary: A dictionary is used to map file extensions to their respective MIME types. This helps handle different file types dynamically.

  • File Extension: We extract the extension of the file using Path.GetExtension() and use it to find the correct MIME type from the dictionary.

  • Setting Content-Type: If the extension is found in the dictionary, the corresponding MIME type is used for the Content-Type header.

Why Explicitly Set Content-Type?

In many cases, the content type is not automatically set, especially when uploading files. Some web APIs require you to specify the correct MIME type for the file to ensure that the server handles it correctly. If the content type is missing or incorrect, the server might reject the request.

Using FileExtensionContentTypeProvider

.NET provides a built-in class called FileExtensionContentTypeProvider, which can automatically map file extensions to MIME types. You can use this class instead of manually managing the dictionary if you want to use a comprehensive list of MIME types. This class is part of the Microsoft.AspNetCore.StaticFiles package.

Example using FileExtensionContentTypeProvider:

var provider = new FileExtensionContentTypeProvider();
if (!provider.TryGetContentType(filePath, out string contentType))
{
    contentType = "application/octet-stream"; // Default MIME type if not found
}

var fileStreamContent = new StreamContent(File.OpenRead(filePath));
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);

In this case, the TryGetContentType method will automatically set the correct content type based on the file's extension.