WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

長い日本語の添付ファイル名が文字化け

by WebSurfer 2011年10月30日 22:00

.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 文字の制限を越えてしまいますので、メーラーによっては対応が必要かもしれません)。

Tags: ,

.NET Framework

TreeView のノードの上下スペーシング

by WebSurfer 2011年10月30日 15:16

TreeView に表示されるノードの上下のスペーシングを調整するにはどうすればいいでしょうか。

TreeView のノード間のスペーシングを調整

子ノードを親ノードに接続する線を表示しない(TreeView.ShowLines プロパティが false)なら NodeStyle-NodeSpacing か NodeStyle-VerticalPadding で設定可能です。

しかし、接続線を表示する場合はそれではうまくいきません。

下の画像は、NodeStyle-VerticalPadding を 10px に設定した TreeView を表示している IE9 とその開発者ツールです(重ねて表示しています)。

NodeStyle-VerticalPadding を 10px に設定

NodeStyle-VerticalPadding を 10px に設定したことにより、td 要素の上下の padding が 10px に設定されてスペーシングは広がったものの、接続線の画像がデフォルトのままなので、線がつながっていません。

線がつながるようにするには、接続線の画像のサイズを大きくする必要があります。(というより、NodeStyle-NodeSpacing や NodeStyle-VerticalPadding は使わないで、画像のサイズだけでスペーシングを調整するようにします。)

接続線の画像は、デフォルトでは TreeView コントロールの埋め込みリソースから 19 x 20 サイズの GIF ファイルがダウンロードされるようになっていますが、以下の手順で変更可能です。

  1. デザイン画面で TreeView の > 印をクリック。
  2. 「TreeView タスク」メニューが表示されるので、[線のイメージのカスタマイズ ...]をクリック。
  3. 「ASP.NET TreeView ラインイメージジェネレーター」ダイアログが表示されるので、Height を指定する(デフォルトは 20)。必要なら、幅も Width の設定で変えられます(デフォルトは 19)。
  4. TreeLineImages フォルダ(存在しなければ自動生成される)の中に、上記 3 で指定したサイズの接続線の GIF イメージファイルが自動的に生成され、配置される(既存の画像ファイルがあれば置き換えられる)。

下の画像は、上記の手順で Height x Width を 40 x 40 に設定して自動生成した接続線の GIF イメージの一部です。

40 x 40 に設定して自動生成した接続線の GIF イメージ

TreeView.LineImagesFolder プロパティに上記 4 の TreeLineImages フォルダを設定し(自動的に設定されます)、上記 3 で画像の幅(Width)を変更していたら TreeView.NodeIndent をそれに合わせて調整します。

でも、これだけではまだ一部で線がつながりません(これはデフォルトの時でも同じです)。下の画像を見てください。

一部で線がつながりません

上の画像が示すように、TreeView.NodeIndent プロパティで設定した幅のインデントを設けるため、td 要素の中に div 要素が配置されています。その場所に接続線がある場合は、その div 要素の中に img 要素を配置して接続線の GIF イメージを表示します。

この例では、NodeIndent="40" としたので div 要素の style 属性で width: 40px; となっています。しかし、height は常に 1px になります。

インデントだけなら height が 1px でも問題ありませんが、接続線を表示する場合は 1px ではダメです。

その問題を解決するには、以下のようなスタイルを定義して、TreeView.CssClass に設定してやります。その結果が一番上の画像です。

<style type="text/css">
    .treeview table td div
    {
        height: 100% !important;
    }    
</style>

Tags: ,

ASP.NET

ページャーの移植

by WebSurfer 2011年10月23日 19:03

BlogEngine.NET バージョン 2.0 に使用されている新しいページャーを、バージョン 1.6.1 のこのブログに移植しました。

新旧ページャーの比較

上の画像では、比較のため、旧ページャー(上の <<前のブログ    次のブログ>> と表示されているもの)と新ページャー(下のページ番号が表示されているもの)を一緒に表示してあります。

具体的な移植方法は以下の通りです。

  • バージョン 2.0 のページャー(カスタムコントロール App_Code/Controls/PostPager.cs)をコピーしてきます。その際、W3C 検証にパスしないAPML フィルターの問題 は処置しておきましょう。また、バージョン 2.0 では名前空間が App_Code.Controls となっているので、バージョン 1.6.1 に合わせて Controls に変更しておきます。
  • User Control/PostList.ascx.cs にバージョン 2.0 のページャーを追加します。さらに、バージョン 1.6.1 のページャーを style="display: none" を追加して消去します。下のサンプルを参考にしてください。
  • User Control/PostList.ascx.cs の Posts プロパティに、ページャーに Post のコレクションへの参照を設定するためのコードを一行追加します。下のサンプルを参考にしてください。
  • 最後に、css を移植します。バージョン 2.0 の Style/Global.css の最後の方にページャー関係のスタイル定義があるので、それをバージョン 1.6.1 の css ファイルにコピーします。バージョン 1.6.1 には Style/Global.css というファイルはないので、自分は、当該テーマの style.css にコピーしました。

User Control/PostList.ascx

<%@ Control Language="C#" 
  AutoEventWireup="true" 
  CodeFile="PostList.ascx.cs" 
  EnableViewState="false" 
  Inherits="User_controls_PostList" %>

<div runat="server" id="posts" class="posts" />

<div id="postPaging" style="display: none">
  <a runat="server" 
    ID="hlPrev" 
    style="float:left">
    << <%=Resources.labels.previousPosts %>
  </a>
  <a runat="server" 
    ID="hlNext" 
    style="float:right">
    <%=Resources.labels.nextPosts %> >>
  </a>
</div>

<%--2011/10/23 BlogEngine.NET 2.0 のページャー 
  (App_Code/Controls/PostPager.cs) を移植。
  旧ページャーは div id="postPaging" に 
  style="display: none" として消去。--%>
<div style="clear:both; display:block">
  <blog:PostPager ID="pager1" 
    runat="server">
  </blog:PostPager>
</div>

User Control/PostList.ascx.cs

// ・・・前略・・・

public partial class User_controls_PostList : UserControl
{

  // ・・・中略・・・

  private List<IPublishable> _Posts;
  /// <summary>
  /// The list of posts to display.
  /// </summary>
  public List<IPublishable> Posts
  {
    get 
    { 
      return _Posts; 
    }
    set 
    { 
      _Posts = value;

      // 2011/10/23 BlogEngine.NET 2.0 のページャー
      // (App_Code/Controls/PostPager.cs) を移植した
      // ので以下のコードを追加。
      this.pager1.Posts = value;
    }

  // ・・・中略・・・

}

Tags:

BlogEngine.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年3月  >>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar