Table of contents

How do I use HttpClient (C#) to send a multipart/form-data request?

To send a multipart/form-data request using HttpClient in C#, you need to use the MultipartFormDataContent class. This is commonly used for file uploads and form submissions that contain both text data and binary content. This guide provides comprehensive examples for various scenarios.

Quick Example

using var httpClient = new HttpClient();
using var multipartContent = new MultipartFormDataContent();

// Add text fields
multipartContent.Add(new StringContent("John Doe"), "username");
multipartContent.Add(new StringContent("user@example.com"), "email");

// Add file from byte array
var fileBytes = File.ReadAllBytes("document.pdf");
multipartContent.Add(new ByteArrayContent(fileBytes), "file", "document.pdf");

var response = await httpClient.PostAsync("https://api.example.com/upload", multipartContent);

Step-by-Step Implementation

Step 1: Set up HttpClient

Use HttpClientFactory or a singleton HttpClient instance to avoid socket exhaustion:

using System.Net.Http;

// Option 1: Using HttpClientFactory (recommended for dependency injection)
public class FileUploadService
{
    private readonly HttpClient _httpClient;

    public FileUploadService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
}

// Option 2: Static instance for simple console applications
private static readonly HttpClient client = new HttpClient();

Step 2: Create MultipartFormDataContent

Build your multipart content by adding different types of data:

using var multipartContent = new MultipartFormDataContent();

// Text fields
multipartContent.Add(new StringContent("John Doe"), "name");
multipartContent.Add(new StringContent("Description text"), "description");

// Text field with specific encoding
multipartContent.Add(new StringContent("Special chars: àáâã", Encoding.UTF8), "title");

// JSON data as string
var jsonData = JsonSerializer.Serialize(new { id = 123, active = true });
multipartContent.Add(new StringContent(jsonData, Encoding.UTF8, "application/json"), "metadata");

Step 3: Add Files

There are several ways to add files to your multipart request:

From File Path (using FileStream)

var filePath = "path/to/document.pdf";
using var fileStream = File.OpenRead(filePath);
using var streamContent = new StreamContent(fileStream);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
multipartContent.Add(streamContent, "document", Path.GetFileName(filePath));

From Byte Array

var fileBytes = File.ReadAllBytes("image.jpg");
var byteArrayContent = new ByteArrayContent(fileBytes);
byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
multipartContent.Add(byteArrayContent, "image", "image.jpg");

From Memory Stream

using var memoryStream = new MemoryStream();
// ... populate memory stream with data
memoryStream.Position = 0;

using var streamContent = new StreamContent(memoryStream);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
multipartContent.Add(streamContent, "textfile", "data.txt");

Step 4: Send the Request

try
{
    var requestUri = "https://api.example.com/upload";
    using var response = await httpClient.PostAsync(requestUri, multipartContent);

    response.EnsureSuccessStatusCode();
    var responseContent = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"Upload successful: {responseContent}");
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"HTTP error: {ex.Message}");
}
catch (TaskCanceledException ex)
{
    Console.WriteLine($"Request timeout: {ex.Message}");
}

Complete Examples

File Upload with Form Data

using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

public class FileUploadExample
{
    private static readonly HttpClient client = new HttpClient();

    public static async Task Main(string[] args)
    {
        await UploadFileWithFormData();
    }

    private static async Task UploadFileWithFormData()
    {
        try
        {
            using var multipartContent = new MultipartFormDataContent();

            // Add form fields
            multipartContent.Add(new StringContent("John Doe"), "uploaderName");
            multipartContent.Add(new StringContent("Important document"), "description");
            multipartContent.Add(new StringContent("documents"), "category");

            // Add file
            var filePath = "document.pdf";
            if (File.Exists(filePath))
            {
                var fileBytes = await File.ReadAllBytesAsync(filePath);
                var fileContent = new ByteArrayContent(fileBytes);
                fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
                multipartContent.Add(fileContent, "file", Path.GetFileName(filePath));

                // Send request
                var response = await client.PostAsync("https://api.example.com/documents", multipartContent);
                response.EnsureSuccessStatusCode();

                var result = await response.Content.ReadAsStringAsync();
                Console.WriteLine($"Upload successful: {result}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}

Multiple File Upload

public async Task UploadMultipleFiles(string[] filePaths)
{
    using var multipartContent = new MultipartFormDataContent();

    // Add text field
    multipartContent.Add(new StringContent("Batch upload"), "operation");

    // Add multiple files
    foreach (var filePath in filePaths)
    {
        if (File.Exists(filePath))
        {
            var fileBytes = await File.ReadAllBytesAsync(filePath);
            var fileContent = new ByteArrayContent(fileBytes);

            // Set appropriate content type based on file extension
            var contentType = GetContentType(Path.GetExtension(filePath));
            fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);

            // Use "files[]" for multiple files or unique names
            multipartContent.Add(fileContent, "files[]", Path.GetFileName(filePath));
        }
    }

    var response = await client.PostAsync("https://api.example.com/batch-upload", multipartContent);
    response.EnsureSuccessStatusCode();
}

private static string GetContentType(string extension)
{
    return extension.ToLower() switch
    {
        ".pdf" => "application/pdf",
        ".jpg" or ".jpeg" => "image/jpeg",
        ".png" => "image/png",
        ".txt" => "text/plain",
        ".json" => "application/json",
        _ => "application/octet-stream"
    };
}

With Authentication Headers

public async Task UploadWithAuth(string accessToken, string filePath)
{
    // Set authentication header
    client.DefaultRequestHeaders.Authorization = 
        new AuthenticationHeaderValue("Bearer", accessToken);

    using var multipartContent = new MultipartFormDataContent();

    // Add file
    var fileBytes = await File.ReadAllBytesAsync(filePath);
    var fileContent = new ByteArrayContent(fileBytes);
    multipartContent.Add(fileContent, "file", Path.GetFileName(filePath));

    var response = await client.PostAsync("https://api.example.com/secure-upload", multipartContent);
    response.EnsureSuccessStatusCode();
}

Advanced Scenarios

Custom Boundary String

var boundary = "----CustomBoundary" + DateTime.Now.Ticks.ToString("x");
using var multipartContent = new MultipartFormDataContent(boundary);

Progress Tracking for Large Files

public class ProgressStreamContent : StreamContent
{
    private readonly IProgress<long> _progress;

    public ProgressStreamContent(Stream content, IProgress<long> progress) 
        : base(content)
    {
        _progress = progress;
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        // Implement progress tracking during upload
        return base.SerializeToStreamAsync(stream, context);
    }
}

Error Handling Best Practices

public async Task<bool> SafeUpload(string filePath)
{
    try
    {
        using var multipartContent = new MultipartFormDataContent();

        // Validate file exists and size
        var fileInfo = new FileInfo(filePath);
        if (!fileInfo.Exists)
        {
            Console.WriteLine("File not found");
            return false;
        }

        if (fileInfo.Length > 10 * 1024 * 1024) // 10MB limit
        {
            Console.WriteLine("File too large");
            return false;
        }

        var fileBytes = await File.ReadAllBytesAsync(filePath);
        var fileContent = new ByteArrayContent(fileBytes);
        multipartContent.Add(fileContent, "file", fileInfo.Name);

        using var response = await client.PostAsync("https://api.example.com/upload", multipartContent);

        if (response.IsSuccessStatusCode)
        {
            Console.WriteLine("Upload successful");
            return true;
        }
        else
        {
            var errorContent = await response.Content.ReadAsStringAsync();
            Console.WriteLine($"Upload failed: {response.StatusCode} - {errorContent}");
            return false;
        }
    }
    catch (HttpRequestException ex)
    {
        Console.WriteLine($"Network error: {ex.Message}");
        return false;
    }
    catch (UnauthorizedAccessException ex)
    {
        Console.WriteLine($"File access error: {ex.Message}");
        return false;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Unexpected error: {ex.Message}");
        return false;
    }
}

Important Notes

  • Resource Management: Always use using statements or manually dispose of StreamContent, ByteArrayContent, and MultipartFormDataContent
  • Content-Type: Set appropriate ContentType headers for files to ensure proper handling by the server
  • File Size: Consider implementing file size limits and progress tracking for large uploads
  • Security: Validate file types and implement virus scanning for production applications
  • Performance: Use ByteArrayContent for small files and StreamContent for large files to optimize memory usage
  • Authentication: Add authentication headers before making the request if required by the API

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon