Search code examples
c#multipartform-datadotnet-httpclient.net-6.0

.NET 6.0 Posting MultipartFormDataContent with HttpClient result in 'Error while copying content to a stream'


I am trying to POST a specific file using HttpClient (.NET 6.0) and MultipartFormDataContent, but I keep getting the following error: Error while copying content to a stream

However, when uploading the file through Postman or through the server's own web portal, it was successful. Unfortunately I don't have access to the remote server to know why it is failing.

Below is the sample code with some information removed.

public static class Program {
    private static HttpClient client;
    private static HttpClientHandler httpClientHandler;

    private static async Task Main() {
        httpClientHandler = new HttpClientHandler() {
            ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
        };

        client = new HttpClient(httpClientHandler);
        client.BaseAddress = new Uri("https://remoteServerIpAddress/");

        var filePath = "absoluteFilePath";
        var fileBytesArray = File.ReadAllBytes(filePath);

        var multipartFormContent = new MultipartFormDataContent();
        multipartFormContent.Add(new ByteArrayContent(fileBytesArray));

        // Setup headers for authorization

        // Error occurs here
        var uploadResponse = await client.PostAsync("api/upload", multipartFormContent).ConfigureAwait(true);

        Console.WriteLine(await uploadResponse.Content.ReadAsStringAsync());
    }
}

Here is the following stacktrace of the error:

System.Net.Http.HttpRequestException
  HResult=0x80131620
  Message=Error while copying content to a stream.
  Source=System.Net.Http
  StackTrace:
   at System.Net.Http.HttpContent.<<CopyToAsync>g__WaitAsync|56_0>d.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
   at System.Net.Http.MultipartContent.<SerializeToStreamAsyncCore>d__23.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter.GetResult()
   at System.Net.Http.HttpContent.<<CopyToAsync>g__WaitAsync|56_0>d.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
   at System.Net.Http.HttpConnection.<SendRequestContentAsync>d__68.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter.GetResult()
   at System.Net.Http.HttpConnection.<SendAsyncCore>d__62.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Net.Http.HttpConnection.<SendAsyncCore>d__62.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at System.Net.Http.HttpConnectionPool.<SendWithVersionDetectionAndRetryAsync>d__83.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1.ConfiguredValueTaskAwaiter.GetResult()
   at System.Net.Http.RedirectHandler.<SendAsync>d__4.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at System.Net.Http.HttpClient.<<SendAsync>g__Core|83_0>d.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at Program.<Main>d__2.MoveNext() in C:\Projects\UploadApplication\Program.cs:line 59

Inner Exception 1:
IOException: Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host..

Inner Exception 2:
SocketException: An existing connection was forcibly closed by the remote host.

I've seen a similar post, but did not see any solution that would help with my situation:

Posting MultipartFormDataContent request with HttpClient gets Error while copying content to a stream

I am not sure if the problem is in my code, but any thoughts or ideas would be appreciated.


Solution

  • I've identified the issue, and it turns out MultipartFormDataContent is adding double quotes around the boundary value by default, while both the server web portal and Postman did not.

    This is an example of what the Content-Type for MultipartFormDataContent would look like if inspected:

    multipart/form-data; boundary="85707dac-07d6-4978-8a35-32c09f6c5b7c"

    However, the remote server was expecting no quotes in the boundary value, like the following:

    multipart/form-data; boundary=85707dac-07d6-4978-8a35-32c09f6c5b7c

    To get around this problem, I've removed the double quotes around the boundary field with the following:

    var contentType = multipartFormContent.Headers.ContentType.Parameters.First();
    contentType.Value = contentType.Value.Replace("\"", String.Empty);
    

    This Github issue is what got my attention into looking at the boundary value: https://github.com/dotnet/aspnetcore/issues/41237

    This post is where I got the solution to remove the double quotes from the boundary: https://stackoverflow.com/a/48371123