Search code examples
c#streamhttpclientmultipartform-datamemorystream

HTTP POST multipart/formdata using HttpClient


When I post the below code using httpclient

using var formContent = new MultipartFormDataContent("NKdKd9Yk");
using var stream = new MemoryStream();
file.CopyTo(stream);
var fileBytes = stream.ToArray();
formContent.Headers.ContentType.MediaType = "multipart/form-data";
formContent.Add(new StreamContent(stream), "file", fileName);
var response = await httpClient.PostAsync(GetDocumentUpdateRelativeUrl(), formContent);

here file is of type IFormfile

In API side I retrieve file as follows

var base64str= "";
using (var ms = new MemoryStream())
{
    request.file.CopyTo(ms);
    var fileBytes = ms.ToArray();
    base64str= Convert.ToBase64String(fileBytes);
    // act on the Base64 data
}

I get 0 byte. My questions is what's wrong with this approch?

But If I use below code. Then API works and I get what I post.

using var formContent = new MultipartFormDataContent("NKdKd9Yk");
using var stream = new MemoryStream();
file.CopyTo(stream);
var fileBytes = stream.ToArray();
formContent.Headers.ContentType.MediaType = "multipart/form-data";
formContent.Add(new StreamContent(new MemoryStream(fileBytes)), "file", fileName);

differece is how I add stream content

formContent.Add(new StreamContent(stream), "file", fileName);
vs
formContent.Add(new StreamContent(new MemoryStream(fileBytes)), "file", fileName);

Why the first approch didn't work but second one does?


Solution

  • You need to add stream.Seek(0, SeekOrigin.Begin); in order to jump back to the beginning of the MemoryStream. You should also use CopyToAsync

    In the second version, you had a fresh MemoryStream from the byte[] array, which is positioned on 0 anyway.

    using var formContent = new MultipartFormDataContent("NKdKd9Yk");
    using var stream = new MemoryStream();
    await file.CopyToAsync(stream);
    stream.Seek(0, SeekOrigin.Begin);
    formContent.Headers.ContentType.MediaType = "multipart/form-data";
    formContent.Add(new StreamContent(stream), "file", fileName);
    using var response = await httpClient.PostAsync(GetDocumentUpdateRelativeUrl(), formContent);
    
    

    Although to be honest, the MemoryStream seems entirely unnecessary here. Just pass the a Stream from file directly.

    using var formContent = new MultipartFormDataContent("NKdKd9Yk");
    formContent.Headers.ContentType.MediaType = "multipart/form-data";
    using var stream = file.OpenReadStream();
    formContent.Add(new StreamContent(stream), "file", fileName);
    using var response = await httpClient.PostAsync(GetDocumentUpdateRelativeUrl(), formContent);