.NET Framework 4 の SmtpClient を使用すると、添付ファイルに長い日本語のファイル名を使うと文字化けするという問題があります。(.NET 3.5 以前では問題なし)
原因はファイル名が二重エンコーディングされてしまうからで、そのメカニズムは このブログ に詳しく書いてあります。
この問題は、米国の MSDN フォーラムでも報告されており、原因が .NET Framework 4 のバグであることは Microsoft も認識しているようです。
その時の Microsoft の人の回答(2010/7/23 付けの記事参照)では、次のリリースを待つか、短い名前にするしかないということでした。
とは言え、現実に問題ない PC があるのは確かですので(自分の開発マシンがそうです)、何らかの更新プログラムがリリースされていて、問題が発生しない PC には適用されているはずと思って調べたら、Connect のページ に、Hotfix KB2183292 によってファイル名の二重にエンコーディングの問題も解決されるとの話がありました。
試しに、二重エンコーディングの問題の出ていた PC に、Hotfix KB2183292 をダウンロードしてインストールすると問題が解決しました。
Windows Update でインストールされる .NET Framework 4 用の更新プログラム で Hotfix KB2183292 が置き換えられると書いてありましたので、この更新プログラムによって二重エンコーディングの問題も解決されているのかと思いましたが、どうもそうではないようです。
実際、.NET Framework 4 用の更新プログラム がインストールされている PC でも二重エンコーディングの問題が発生しました。
というわけで、Windows Update で解決されているのではなさそうなので、次期リリース(.NET 4.5 ?)でこの問題が解決されるまでは、長い日本語のファイル名を添付ファイルに使う場合は、何らかの対策が必要なようです。
問題は、Attachment コンストラクタ (String, ContentType) で長い日本語のファイル名を使用すると二重エンコーディングされるということなので、代わりに Attachment コンストラクタ (Stream, ContentType) を使用し、Content-Disposition の filename の方にファイル名を設定することで問題を回避できるはずです。
以下のような感じです。
private string EncodeHeader(string str, Encoding enc)
{
string ret =
System.Convert.ToBase64String(enc.GetBytes(str));
ret = string.Format("=?{0}?B?{1}?=", enc.BodyName, ret);
return ret;
}
private void button2_Click(object sender, EventArgs e)
{
System.Text.Encoding enc =
System.Text.Encoding.GetEncoding("utf-8");
string mailAddress = this.userName + "@" + this.host;
MailAddress to =
new MailAddress(mailAddress, EncodeHeader("日本花子", enc));
MailAddress from =
new MailAddress(mailAddress, EncodeHeader("日本太郎", enc));
MailMessage mailMessage = new MailMessage(from, to);
mailMessage.Subject = textBox1.Text;
mailMessage.Body = textBox2.Text;
// 添付ファイル
file = "テスト用の長めのファイル名です.pdf";
FileStream fs =
new FileStream(file, FileMode.Open, FileAccess.Read);
Attachment data =
new Attachment(fs, MediaTypeNames.Application.Octet);
ContentDisposition disposition = data.ContentDisposition;
disposition.FileName =
EncodeHeader(System.IO.Path.GetFileName(file), enc);
mailMessage.Attachments.Add(data);
SmtpClient smtpClient = new SmtpClient();
smtpClient.Host = host;
smtpClient.Port = portSmtp;
try
{
smtpClient.Send(mailMessage);
MessageBox.Show("Your mail was sent.");
}
catch (SmtpException ex)
{
SmtpStatusCode status = ex.StatusCode;
MessageBox.Show("SmtpException: " + ex.Message +
"\nSmtpStatusCode: " + status.ToString());
}
catch (Exception ex)
{
MessageBox.Show("Exception: " + ex.Message);
}
finally
{
mailMessage.Dispose();
}
}
こうすると、以下のように Content-Type の name は application/octet-stream となり、Content-Disposition の filename にファイル名が設定され、正しいファイル名を取得できます(メーラーによってはダメかもしれませんが)。
Content-Type: application/octet-stream;
name="application/octet-stream"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="=?utf-8?B?44OG44...BkZg==?="
なお、一旦 Cotent-Type の name が二重エンコーディングされたものになってしまうと、Content-Disposition の filename にファイル名を設定しても、何故かそちらは無視されてしまいます。メーラーにもよると思いますが Windows メールはダメでした。
という訳で、上記のコード方法が次期バージョンがリリースされるまでの Workaround としては適当なようです(ただし、filename のところが、一行で 76 文字の制限を越えてしまいますので、メーラーによっては対応が必要かもしれません)。