Monsignor
Monsignor

Reputation: 2947

HttpClient wrong encoding in Content-Disposition

I am POST-ing an image with HttpClient and it works well for files with Latin names, but as soon as a name contains any non-ASCII characters it gets transformed to a sequence of question marks. If I create an html form and use a browser to post the file, the file name is sent in UTF8 and the target server perfectly accepts it.

using (var client = new HttpClient())
{
    var streamContent = new StreamContent(someImageFileStream);
    streamContent.Headers.Add(
        "Content-Disposition",
        "form-data; name=\"image\"; filename=\"Тест.jpg\"");

    var content = new MultipartFormDataContent();
    content.Add(streamContent);

    await client.PostAsync("http://localhost.fiddler/", content);
}

This produces the following request:

POST http://localhost/ HTTP/1.1
Content-Type: multipart/form-data; boundary="e6fe89be-e652-4fe3-8859-8c7a339c5550"
Host: localhost
Content-Length: 10556

--e6fe89be-e652-4fe3-8859-8c7a339c5550
Content-Disposition: form-data; name="image"; filename="????.jpg"

...here goes the contents of the file...

I understand that HttpClient might work according to some standard, but anyway, is there any workaround?

UPDATE: The external API doesn't want to accept the format filename*=utf-8''Тест.jpg, it expects filename="Тест.jpg".

Upvotes: 5

Views: 6846

Answers (4)

Klara Smiskova
Klara Smiskova

Reputation: 21

If it helps, you can also remove the "filename*"

//It deletes filename* parametr
foreach (var content in multipartContent) {
   var headerContent = content.Headers.ContentDisposition.Parameters.Where(x => x.Name == "filename*").SingleOrDefault();
   if(headerContent != null)
      content.Headers.ContentDisposition.Parameters.Remove(headerContent);
}
                            

Upvotes: 2

idilov
idilov

Reputation: 565

This is another way to workaround the limitation of HttpClient without tampering with internal fields. Inspired by this answer.

using (var client = new HttpClient())
{
    var streamContent = new StreamContent(someImageFileStream);
    streamContent.Headers.Add("Content-Disposition",
        new string(Encoding.UTF8.GetBytes("form-data; name=\"image\"; filename=\"Тест.jpg\"").
        Select(b => (char)b).ToArray()));

    var content = new MultipartFormDataContent();
    content.Add(streamContent);
    await client.PostAsync("http://localhost.fiddler/", content);
}

I confirm that even .net core 2.2 doesn't have proper support for uploading files whose names contain non-ASCII characters. HttpClient does work according to some standard but Java servers don't care about that standard and expect UTF-8 formatted headers.

Upvotes: 9

Monsignor
Monsignor

Reputation: 2947

OK, I've found a way to force MultipartFormDataContent to forget the ancient RFCs and use UTF8 instead. The trick is to use reflection to overwrite the DefaultHttpEncoding defined in the internal static class HttpRuleParser.

typeof(HttpClient)
  .Assembly
  .GetType("System.Net.Http.HttpRuleParser")
  .GetField("DefaultHttpEncoding", BindingFlags.Static | BindingFlags.NonPublic)
  .SetValue(null, System.Text.Encoding.UTF8);

Not sure which bad consequences that might cause, but I suppose there are none.

Upvotes: 3

Tom Blodget
Tom Blodget

Reputation: 20772

Instead of adding a header that you built yourself, use the .NET library:

streamContent.Headers.ContentDisposition = 
    new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data") { 
        Name = "image", 
        FileName = "Тест.jpg" };

That creates the header per the web docs and RFC5987.

Content-Disposition: form-data; name=image; filename="=?utf-8?B?0KLQtdGB0YIuanBn?="

Upvotes: 1

Related Questions