MSDN フォーラムで、IE で xlsm をダウンロードすると zip 形式になってしまい、直接 Excel で開けないという話があったので、ちょっと調べてみました。
結局、単にそれはサーバー側の Web アプリの問題だったのですが、調査の結果、自分が知らなかったことがいろいろあったので、忘れないように書いておきます。
ブラウザは、ヘッダーの Content-Disposition: attachment; filename= で指定する拡張子と、Content-Type: で指定する MIME Type のどちらでファイルの種類を判断するでしょうか?
検証した結果は以下のとおりでした。簡単に言うと、IE8 は拡張子で判断、Opera は MIME Type で判断、Forefox は拡張子と MINE Type の指定が違う場合結果は未定義のようです。
ブラウザ
|
Ext: xlsm MT: xlsm
|
Ext: xlsm MT: zip
|
Ext: zip MT: zip
|
Ext: zip MT: xlsm
|
IE8
|
Excel
|
Excel
|
Zip
|
Zip
|
Firefox 3.6.8
|
Excel
|
Zip
|
Zip
|
Zip
|
Opera 10.61
|
Excel
|
Zip
|
Zip
|
Excel
|
上の表で、Ext は Content-Disposition に設定した拡張子、MT は Content-Type に設定した MIME Type です。
検証に使ったファイルは、有効な xlsm 形式のファイルで、拡張子は正しく xlsm としており、検証の間、中身も名前も一切変えていません。
クライアントの OS は Vista SP2 です。拡張子とアプリケーションの関連付けは、xlms は Excel、zip は WinZip としています。Web サーバーは Windows Server 2008 の IIS7, ASP.NET 3.5 SP1 です。
予想外の動きをしたのは Opera で、拡張子と MIME Type が異なる場合、拡張子を MIME Type に合わせて書き換えてしまいます。下の画像は、拡張子を xlsm、MIME Type を zip とした場合の例です。元のファイル名 001.xlsm が 001.zip に書き換えられています。
直リンクの場合(a 要素の href 属性にサーバーに置いた xlsm ファイルを指定した場合)、ヘッダーは以下のようになります。ブラウザは正しく判断出来るようです。
Content-Type: application/vnd.ms-excel.sheet.macroEnabled.12
Last-Modified: Thu, 02 Sep 2010 13:07:58 GMT
Accept-Ranges: bytes
ETag: "3e8647dd9f4acb1:0"
X-Powered-By: ASP.NET
Date: Thu, 02 Sep 2010 13:12:12 GMT
Content-Length: 12477
ちなみに、HTTP ハンドラーを使った場合のヘッダーは以下のようになります。
Cache-Control: private
Content-Type: application/vnd.ms-excel.sheet.macroEnabled.12
Content-Disposition: attachment; filename=001.xlsm
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
Date: Thu, 02 Sep 2010 13:21:00 GMT
Content-Length: 12477
検証に使った HTTP ハンドラのコードを参考までにアップしておきます。
<%@ WebHandler Language="C#" Class="_045_Handler" %>
using System;
using System.Web;
using System.IO;
public class _045_Handler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string path =
context.Server.MapPath("~/Test/data/001.xlsm");
if (!File.Exists(path))
{
return;
}
string type = context.Request.QueryString["type"];
switch (type)
{
case "xlsm_xlsm":
SetHeader(context, "001.xlsm",
"application/vnd.ms-excel.sheet.macroEnabled.12");
break;
case "xlsm_zip":
SetHeader(context, "001.xlsm",
"application/x-zip-compressed");
break;
case "zip_zip":
SetHeader(context, "001.zip",
"application/x-zip-compressed");
break;
case "zip_xlsm":
SetHeader(context, "001.zip",
"application/vnd.ms-excel.sheet.macroEnabled.12");
break;
default:
SetHeader(context, "001.xlsm",
"application/vnd.ms-excel.sheet.macroEnabled.12");
break;
}
context.Response.TransmitFile(path);
context.Response.End();
}
public bool IsReusable
{
get {
return false;
}
}
protected void SetHeader(HttpContext context, string name, string type)
{
context.Response.AppendHeader("Content-Disposition",
"attachment; filename=" + name);
context.Response.ContentType = type;
}
}
他に、今回知った新(?)事実は、Excel 2007 からファイル形式が変わったということです。何をいまさらと言われるかも知れませんが。(汗)
Excel 2007 からはファイルはバイナリではなくなり、xml 形式のテキストを zip で圧縮したものになっているそうです。確かに WinZip で xlsm ファイルを開くことができ、中身は xml 形式のテキストファイルでした。
また、xlsm は「マクロ有効ブック」というそうで、セキュリティ対策として、拡張子からマクロが含まれていることが分かるような名前の規則を作ったそうです。
------------ 2011/5/21 追記 ------------
IE8 は拡張子で判断と書きましたが、やはり IE はそのような仕様になっているようです。以下のページを参考にしてください。
ファイルのダウンロードダイアログで表示されるファイル名の命名規則
即ち、上のページの「詳細」の 1 番目に書いてあるとおり「Content-Disposition: ヘッダが存在する場合は、filename パラメータで設定されたファイル名が利用されます。」ということです。
サポートページには書いてないですが、Content-Disposition: ヘッダでファイル名が指定してあれば、Content-Type: の指定は無視されます。
2 番目の「HTTP レスポンス内に存在する、送られてきた Content-Type が、レジストリの HKEY_CLASS_ROOT\MIME\Database\Content Type 以下に存在するかどうか・・・」というのは、Content-Disposition: ヘッダの filename パラメータにファイル名の指定がない場合に限るようです。