How do I attach a file to a request using HttpClient (C#)?

To attach a file to an HttpClient request in C#, you'll use MultipartFormDataContent to create a multipart/form-data request. This is the standard approach for file uploads in web applications.

Basic File Upload

Here's a complete example of uploading a file:

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

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

    public static async Task UploadFileAsync()
    {
        string url = "https://api.example.com/upload";
        string filePath = @"C:\Documents\document.pdf";

        try
        {
            using var multipartContent = new MultipartFormDataContent();

            // Add the file
            using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            using var fileContent = new StreamContent(fileStream);

            // Set content type (optional but recommended)
            fileContent.Headers.ContentType = 
                System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/pdf");

            multipartContent.Add(fileContent, "file", Path.GetFileName(filePath));

            // Send the request
            var response = await httpClient.PostAsync(url, multipartContent);
            response.EnsureSuccessStatusCode();

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

Multiple Files Upload

You can upload multiple files in a single request:

public static async Task UploadMultipleFilesAsync()
{
    string url = "https://api.example.com/upload-multiple";
    string[] filePaths = { 
        @"C:\Documents\file1.txt", 
        @"C:\Documents\file2.jpg" 
    };

    using var multipartContent = new MultipartFormDataContent();

    foreach (string filePath in filePaths)
    {
        using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        var fileContent = new StreamContent(fileStream);

        // Set appropriate content type
        string contentType = GetContentType(Path.GetExtension(filePath));
        fileContent.Headers.ContentType = 
            System.Net.Http.Headers.MediaTypeHeaderValue.Parse(contentType);

        multipartContent.Add(fileContent, "files", Path.GetFileName(filePath));
    }

    var response = await httpClient.PostAsync(url, multipartContent);
    response.EnsureSuccessStatusCode();
}

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

Upload with Additional Form Data

Often you need to send additional form fields along with files:

public static async Task UploadFileWithFormDataAsync()
{
    string url = "https://api.example.com/upload";
    string filePath = @"C:\Documents\image.jpg";

    using var multipartContent = new MultipartFormDataContent();

    // Add text fields
    multipartContent.Add(new StringContent("John Doe"), "userName");
    multipartContent.Add(new StringContent("Profile Picture"), "description");
    multipartContent.Add(new StringContent("public"), "visibility");

    // Add the file
    using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    using var fileContent = new StreamContent(fileStream);
    fileContent.Headers.ContentType = 
        System.Net.Http.Headers.MediaTypeHeaderValue.Parse("image/jpeg");

    multipartContent.Add(fileContent, "avatar", Path.GetFileName(filePath));

    var response = await httpClient.PostAsync(url, multipartContent);
    response.EnsureSuccessStatusCode();
}

Upload from Byte Array

If you have file data as a byte array instead of a file path:

public static async Task UploadFromByteArrayAsync(byte[] fileData, string fileName)
{
    string url = "https://api.example.com/upload";

    using var multipartContent = new MultipartFormDataContent();
    using var byteContent = new ByteArrayContent(fileData);

    byteContent.Headers.ContentType = 
        System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/octet-stream");

    multipartContent.Add(byteContent, "file", fileName);

    var response = await httpClient.PostAsync(url, multipartContent);
    response.EnsureSuccessStatusCode();
}

Progress Tracking

For large files, you might want to track upload progress:

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

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

    protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        const int bufferSize = 4096;
        var buffer = new byte[bufferSize];
        long totalBytesRead = 0;

        int bytesRead;
        while ((bytesRead = await _stream.ReadAsync(buffer, 0, bufferSize)) > 0)
        {
            await stream.WriteAsync(buffer, 0, bytesRead);
            totalBytesRead += bytesRead;
            _progress?.Report(totalBytesRead);
        }
    }
}

// Usage
public static async Task UploadWithProgressAsync()
{
    string filePath = @"C:\large-file.zip";
    var progress = new Progress<long>(bytesUploaded =>
    {
        Console.WriteLine($"Uploaded {bytesUploaded} bytes");
    });

    using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    using var progressContent = new ProgressStreamContent(fileStream, progress);
    using var multipartContent = new MultipartFormDataContent();

    multipartContent.Add(progressContent, "file", Path.GetFileName(filePath));

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

Best Practices

  1. Use a shared HttpClient instance to avoid socket exhaustion
  2. Always specify Content-Type for better server compatibility
  3. Handle exceptions properly including network timeouts
  4. Dispose resources using using statements
  5. Validate file size before upload to prevent memory issues
  6. Use async/await consistently for better performance

Common Issues

  • File locked: Ensure no other process is using the file
  • Large files: Consider streaming for files over 85KB to avoid Large Object Heap
  • Content-Type: Some servers require specific content types
  • Field names: Match the exact field names expected by the server API

This approach works for most file upload scenarios and is compatible with standard web APIs and REST services.

Related Questions

Get Started Now

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