C#
.NET
HttpClient

HttpClientでmultipart/form-dataでのファイル送信で日本語のファイル名が文字化けする

使用環境

C#
.NET 4.0 (NugetでMicrosoft.Net.Httpをインストール済み)
※たぶん4.5以降も同じです。

やりたかったこと

WebApiをコンソールアプリから呼び出したかったんです。
でもWebClientクラスでは1回のPOST送信で複数ファイル送れないじゃない!

HttpClientならできる?でも.NET4.5以降のものだしなぁ・・・

と思ったらNugetで配信されてた(Microsoftさんありがとう!)

ファイル送信ソース

こちらさんの記事を参考にしながら下記を記述

Program.cs
using (var httpClient = new HttpClient())
using (var multipart = new MultipartFormDataContent())
{
    var finfo = new FileInfo(filePath); // ここは指定のファイルをどうぞ

    var fileContent = new StreamContent(File.OpenRead(finfo.FullName));
    fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
    {
        Name = "file1", // ここは任意の名前で
        FileName = finfo.Name
    }

    multipart.Add(fileContent);

    // 送信
    var postAsyncTask = httpClient.PostAsync(reqestUrl, multipart);
    postAsyncTask.Wait(); // async/awaitは.NET4.0だから使えない...
    var responseMessage = postAsyncTask.Result;
}

ちなみに今回は"サンプルファイル.xlsx"というファイル名のファイルを送信している。

すると受け取り先で...

ファイル名が文字化け!
↓こんな感じ
=?utf-8?B?44K144Oz44OX44Or44OV44Kh44Kk44OrLnhsc3g=?=

対策

いろいろと調べると、勝手にHttpClientくんがファイル名とかを含んだContentDispositionをエンコードしちゃうらしい。
おそらくBase64でエンコードされる。

そこでStack Overflowくんのこの記事を参考にして、対策をおこなう。

Program.cs
using (var httpClient = new HttpClient())
using (var multipart = new MultipartFormDataContent())
{
    var finfo = new FileInfo(filePath); // ここは指定のファイルをどうぞ

    // 手動でエンコードを行う。
    string headerValue = string.Format("form-data; name=\"{0}\"; filename=\"{1}\"", "file1", finfo.Name);
    byte[] headerValueByteArray = Encoding.UTF8.GetBytes(headerValue);
    var encodingHeaderValue = new StringBuilder();
    foreach (byte b in headerValueByteArray)
    {
        encodingHeaderValue.Append((char)b);
    }

    fileContent.Headers.Add("Content-Disposition", encodingHeaderValue.ToString());

    multipart.Add(fileContent);

    // 送信
    var postAsyncTask = httpClient.PostAsync(reqestUrl, multipart);
    postAsyncTask.Wait(); // async/awaitは.NET4.0だから使えない...
    var responseMessage = postAsyncTask.Result;
}

自分のつたない英語力では「こんな感じで先にエンコードしてやるんやで」
ぐらいのニュアンスしかつかめなかったが、とりあえずこれで文字化けしなかったのでよしとする。

結論

HttpClientくんが余計なことをしてるので、こんな感じでやってあげよう。
もしかしたらNugetでHttpClientやWebClientの不便な部分を埋めると多分ヒットすると思う。(自分はむり)

先人の方たちの記事に感謝