ASP.NET MVC アプリケーションでファイルをダウンロードするのはどうすればいいかということを備忘録として書いておきます。(Web Forms アプリの場合は先の記事「ダウンロードは HTTP ハンドラで」を見てください)

上の画像のように通知バーを表示し(IE11 の場合です)、ファイル名とファイルの種類をブラウザにきちんと認識させるのが条件です。
基本的には以下のアクションメソッド FileDownload のようにします。
public ActionResult FileDownload()
{
string fileName = Server.MapPath("~/Files/test.pdf");
return File(fileName, "application/pdf", "test.pdf");
}
上のコードの return File(...); のヘルパーメソッド File は、FileResult クラスを継承した FilePathResult, FileContentResult, FileStreamResult 派生クラスのいずれかを呼び出します。
それらの使い分けは以下の通りです。
-
FilePathResult: コンテンツが既存のファイルとして提供される。
-
FileStreamResult: コンテンツがストリームとして提供される。
-
FileContentResult: コンテンツがバイト配列として提供される。
上記のどれを呼び出すかは return File(...); の引数によります。詳しくは Microsoft のドキュメント Controller.File Method を見てください。
上のコード例では第一引数が文字列(ファイルの物理パス)なので FilePathResult が呼び出されます。
FilePathResult は Web Forms アプリでもファイルをダウンロードする際に使われている TransmitFile メソッドを呼び出します。TransmitFile メソッドは、FilePathResult の第 1 引数に指定されたファイル名を受けて、そのファイルをメモリにバッファリングせずに、HTTP 応答出力ストリームに直接書き込みます。
FileStreamResult は、第 1 引数に指定されるストリームから FileStream.Read メソッドでバイト列を取得し、それを HttpResponse.OutputStream.Write メソッドを使って出力ストリームに書き出すという操作を行います。
FileContentResult は、第 1 引数に指定されるバイト列を直接 HttpResponse.OutputStream.Write メソッドを使って出力ストリームに書き出すという操作を行います。
ファイル名とファイルの種類をブラウザに認識させ、上の画像のような通知を表示させるには、上のサンプルコードで return File(...); の第 2, 3 引数に順にファイルの MIME タイプ、拡張子を含むファイル名を設定します。そうすることにより、応答ヘッダに Content-Type と Content-Disposition が適切に設定されます。
なお、第 2 引数に設定するファイル名には US-ASCII 文字を使用しないと IE では文字化けするので注意してください。先の記事「ダウンロードファイル名の文字化け」に書いた方法でどのように対応できるかは未調査です。
キャッシュコントロールは Web Forms アプリと同様に HttpResponse.Cache で取得できる HttpCachePolicy オブジェクトを使って可能です。
例えば、上の FileDownload アクションメソッドに以下のコードを追加してやれば、
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.Now.ToUniversalTime());
Response.Cache.SetMaxAge(new TimeSpan(0, 0, 0, 0));
以下の画像の応答ヘッダのように Cache-Control: no-cache, Pragma: no-cache, Expires: -1 が設定されます。

File メソッドの第 2, 3 引数に設定した MIME タイプとファイル名により、Content-Type と Content-Disposition が適切に設定されているところにも注目してください。