26

I have backend endpoint Task<ActionResult> Post(IFormFile csvFile) and I need to call this endpoint from HttpClient. Currently I am getting Unsupported media type error. Here is my code:

var filePath = Path.Combine("IntegrationTests", "file.csv");
var gg = File.ReadAllBytes(filePath);
var byteArrayContent = new ByteArrayContent(gg);
var postResponse = await _client.PostAsync("offers", new MultipartFormDataContent
{
    {byteArrayContent }
});
Deivydas Voroneckis
  • 1,973
  • 3
  • 19
  • 40

6 Answers6

29

You need to specify parameter name in MultipartFormDataContent collection matching action parameter name (csvFile) and a random file name

var multipartContent = new MultipartFormDataContent();
multipartContent.Add(byteArrayContent, "csvFile", "filename");
var postResponse = await _client.PostAsync("offers", multipartContent);

or equivalent

var postResponse = await _client.PostAsync("offers", new MultipartFormDataContent {
    { byteArrayContent, "csvFile", "filename" }
});
Alexander
  • 9,104
  • 1
  • 17
  • 41
  • 1
    Thanks I was missing this headers: content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "csvFile", FileName = fileName }; content.Headers.Remove("Content-Type"); content.Headers.Add("Content-Type", "application/octet-stream; boundary=----WebKitFormBoundaryMRxYYlVt8KWT8TU3"); – Deivydas Voroneckis Apr 01 '19 at 15:40
  • 2
    What if my type is not the `IFormFile`, but the `List`? Thank you. – Sasuke Uchiha Sep 15 '20 at 16:13
11

Use this snippet:

const string url = "https://localhost:5001/api/Upload";
const string filePath = @"C:\Path\To\File.png";

using (var httpClient = new HttpClient())
{
    using (var form = new MultipartFormDataContent())
    {
        using (var fs = File.OpenRead(filePath))
        {
            using (var streamContent = new StreamContent(fs))
            {
                using (var fileContent = new ByteArrayContent(await streamContent.ReadAsByteArrayAsync()))
                {
                    fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");

                    // "file" parameter name should be the same as the server side input parameter name
                    form.Add(fileContent, "file", Path.GetFileName(filePath));
                    HttpResponseMessage response = await httpClient.PostAsync(url, form);
                }
            }
        }
    }
}
Moien Tajik
  • 2,115
  • 2
  • 17
  • 39
  • I was missing `MediaTypeHeaderValue` on `fileContent.Headers.ContentType` and `fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");` save my life – eddyP23 Nov 11 '20 at 12:46
  • 2
    3/5 of your using statements could be simplified to one line `var fileContent = new ByteArrayContent(await File.ReadAllBytesAsync(filePath));` – Vargo Jan 08 '21 at 20:15
5

This worked for me as a generic

public static Task<HttpResponseMessage> PostFormDataAsync<T>(this HttpClient httpClient, string url, string token, T data)
    {
        var content = new MultipartFormDataContent();

        foreach (var prop in data.GetType().GetProperties())
        {
            var value = prop.GetValue(data);
            if (value is FormFile)
            {
                var file = value as FormFile;
                content.Add(new StreamContent(file.OpenReadStream()), prop.Name, file.FileName);
                content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = prop.Name, FileName = file.FileName };
            }
            else
            {
                content.Add(new StringContent(JsonConvert.SerializeObject(value)), prop.Name);
            }
        }

        if (!string.IsNullOrWhiteSpace(token))
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        return httpClient.PostAsync(url, content);
    }
Danilo Popovikj
  • 127
  • 2
  • 3
  • Can u write example of api controller ?any custom model required for file with json data – Ajt Aug 25 '20 at 18:10
4

Solved by using this code:

        const string fileName = "csvFile.csv";
        var filePath = Path.Combine("IntegrationTests", fileName);
        var bytes = File.ReadAllBytes(filePath);
        var form = new MultipartFormDataContent();
        var content = new StreamContent(new MemoryStream(bytes));
        form.Add(content, "csvFile");
        content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
        {
            Name = "csvFile",
            FileName = fileName
        };
        content.Headers.Remove("Content-Type");
        content.Headers.Add("Content-Type", "application/octet-stream; boundary=----WebKitFormBoundaryMRxYYlVt8KWT8TU3");
        form.Add(content);

        //Act
        var postResponse = await _sellerClient.PostAsync("items/upload", form);
Deivydas Voroneckis
  • 1,973
  • 3
  • 19
  • 40
3

Please see the following working code with .NET 5.0 Environment.

You send the file as a byte[] and receive it as a IFormFile in the API.

//api controller receiver

[HttpPost("SendBackupFiles")]
public IActionResult SendBackupFiles(IFormFile file)
{
    var filePath = Path.GetTempFileName();

    using (var stream = System.IO.File.Create(filePath)) 
        file.CopyToAsync(stream);
}



//client code sender example, not optimized lol.

const string filePath = @"C:\temp\hallo.csv";

using (var httpClient = new HttpClient())
{
    var form = new MultipartFormDataContent();
               
    byte[] fileData = File.ReadAllBytes(filePath);

    ByteArrayContent byteContent = new ByteArrayContent(fileData);

    byteContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
    
    form.Add(byteContent, "file", Path.GetFileName(filePath));

    var result = httpClient.PostAsync("http://localhost:5070/..yourControllerName.../SendBackupFiles", form).ConfigureAwait(false).GetAwaiter().GetResult().Content.ReadAsStringAsync().Result;
} 
Anubis77
  • 102
  • 1
  • 6
  • This worked out well helping me. What you do here is to wrap IFormFile'ize the byte array. That corresponds to the question asked (i.e. how to adapt sender to the recipient. However, I'm curious, what if we for some reason can't, don't want to or simply won't adapt the sender? It's hardly sufficient to change the type in the controller to `byte[]` instead of `IFormFile`, is it? What would you recommend in such case? – Konrad Viltersten Mar 12 '23 at 10:53
2

Post the attachment as an MultipartFormDataContent

var type = typeof(Startup);
            var stream = type.Assembly.GetManifestResourceStream(type, "Resources.New.docx");
        var fileContent = new StreamContent(stream);
var data = new MultipartFormDataContent
            {
                { fileContent, "file", "New.docx" }
            };

        var response = await _client.PostAsync("upload", multipartContent);

Source: https://medium.com/@woeterman_94/c-webapi-upload-files-to-a-controller-e5ccf288b0ca

Enrico
  • 2,734
  • 1
  • 27
  • 40