IE6でダウンロード

サーバで動的に生成したプレイリストをm3uとしてダウンロードさせるモジュールを作成していてハマる。例えば"http://www.example.com/hoge/foo"といったURLにブラウザでアクセスすると、"bar.m3u"といったファイルのダウンロードが出来るというもので、非常に単純な作りなのだが、FireFoxではすぐに動作が確認出来たものの、IE6でどうにも期待通りの動作にならない。上述のようなURLにアクセスすると、"foo"というファイル名でダウンロードしようとしてしまい、m3uに関連付けられたアプリケーションでそのまま開く事が出来ないのだった。

調べてみると、HTTPヘッダをIEが無視する仕様による問題のようで、Content-Dispositionのfilenameが無視されている模様。Content-Dispositionをattachmentからinlineへ変えてみたり、Content-TypeをContent-Dispositionをapplication/mpegurlではなく、aplication/x-download、application/x-unknown、application/octet-stream-dummy等と指定してみる方法がすぐ発見出来たが、それでは解決せず。

で、一頻り悩んだ結果、見つけたのが下記情報。

Internet Explorer 6 でダウンロード ダイアログに正しいファイル名が表示されない

Microsoft Internet Explorer 6 で HTTP 圧縮が使用された環境では、ダウンロード ダイアログに正しいファイル名が表示されない場合があります。
例 : TEST.ASP にアクセスすると abc.csv がダウンロードされ、かつ以下の HTTP ヘッダーによりファイル名が指定される場合
Content-Disposition:Attachment;filename=abc.csv
	HTTP 圧縮を使用しない環境では Content-Disposition ヘッダーにより指定された "sample.csv" がファイル名として表示されます。
	HTTP 圧縮を使用する環境では、Content-Disposition ヘッダーで指定されたファイル名は使われず、URL で指定した "test.asp" がファイル名として表示されます。

symfonyでは、アプリケーションの設定settings.yml内で、compressedの設定をonとすることにより、HTTP圧縮されたレスポンスを返す事が出来る。(そして、現在対象としているアプリケーションでもこれをonに設定してある。)

Mastering Symfony's Configuration Files

Enables PHP response compression. Set it to on to compress the outgoing HTML via the PHP compression handler.

この設定は、sfConfig::set('sf_compressed', false)等とモジュール内で上書きする事が可能ではあるが、実はコントローラからアプリケーションのconfig.phpが読み込まれる時点で呼び出される、sfCore::bootstrap($sf_symfony_lib_dir, $sf_symfony_data_dir)内で読み込まれるsymfony.php内での下記箇所で、既に出力の圧縮バッファリングは開始されており、それを特定のモジュール内で止めさせる事は、通常は行えない。つまり、アプリケーション内の全てのモジュールに対してHTTP圧縮を有効とするか、若しくは全てを無効とするか、の何れかである。

ob_start(sfConfig::get('sf_compressed') ? 'ob_gzhandler' : '');

という訳で、ダウンロードだけの為にそのアプリケーション内の他の全モジュールに措いてもHTTP圧縮が使えなくなってしまう、というのはナンセンスなので、ダウンロードに関しては別アプリケーションを作成し、compressed設定をoffとしてやることで回避した。ちなみに、あるページでHTTP圧縮が有効になっているかどうかは、port80で簡単に調べられる。

しかし、これで上手く行くかと思いきや、今度はIEではダウンロードそのものが出来なくなってしまった。HTTP圧縮が有効だった場合は、期待したファイル名ではないものの、ダウンロード自体は出来ていた。今度はダイアログに下記のようなメッセージが表示され、ダウンロード自体が不可能になってしまった。

このインターネットのサイトを開くことができませんでした。要求されたサイトが使用できないか、見つけることができません。後でやり直してください。

そこで、次に探し出したのが下記情報。

Content-Disposition: attachemnt と Cache-Control: no-cache によるダウンロードの問題

Internet Explorer を使用して下記条件を満たすファイルを開いた場合、ファイル名が見つからない内容のエラーが発生し、ファイルを開くことができない場合があります。
	ダウンロード対象となるファイルに Content-Disposition:attachment ヘッダーを付加している
	Cache-Control:no-cache ヘッダーなどを使用して、ファイルのキャッシュを行わない設定をしている
回避策
Web サーバーで Content-Disposition に inline を指定する等、Content-Disposition:attachment ヘッダーを使用しない、またはキャッシュを制限しないことにより現象を回避することが可能です。

なるほど。ここはCache-Controlにpublicを指定して、ようやく解決。IE6でも期待通りのファイル名で無事ダウンロード出来るようになった。

纏めると、IE6で動的に生成したファイルのダウンロードを行う(URLに対応する実ファイルが存在しないファイルのダウンロードを行う)ためには、下記に注意する。

  • Cache-Controlをpublicにする。
  • HTTP圧縮を行わない。
  • Content-Typeは普通に指定して問題なし。(MIME-Typeによっては問題があるかも知れないが。)
  • Content-Dispositionはattachmentで問題なし。

これでContent-Dispositionのfilenameに従ったファイルがダウンロード出来る、多分。