MVCコントローラーからダウンロードするファイルを提示するにはどうすればよいですか?


109

Webフォームでは、通常、次のようなコードを使用して、ブラウザーにPDFなどの任意のファイルタイプとファイル名を含む「ダウンロードファイル」ポップアップを表示させます。

Response.Clear()
Response.ClearHeaders()
''# Send the file to the output stream
Response.Buffer = True

Response.AddHeader("Content-Length", pdfData.Length.ToString())
Response.AddHeader("Content-Disposition", "attachment; filename= " & Server.HtmlEncode(filename))

''# Set the output stream to the correct content type (PDF).
Response.ContentType = "application/pdf"

''# Output the file
Response.BinaryWrite(pdfData)

''# Flushing the Response to display the serialized data
''# to the client browser.
Response.Flush()
Response.End()

ASP.NET MVCで同じタスクを実行するにはどうすればよいですか?

回答:


181

ファイルが存在するか、オンザフライで作成するかに応じて、アクションからFileResultまたはFileStreamResultを返します。

public ActionResult GetPdf(string filename)
{
    return File(filename, "application/pdf", Server.UrlEncode(filename));
}

14
これは、ASP.NET MVCがすばらしい理由の好例です。以前は9行のわかりにくいコードでしなければならなかったことを1行で行うことができます。とても簡単です!
ジョンクルーガー、

tvanfossonに感謝します。これを行うための最良の解決策を探しました。これは素晴らしいことです。
Mark Kadlec、2010

1
これには、ファイル名にファイル拡張子が必要です。そうでない場合、ファイル名とコンテンツタイプは完全に無視され、ファイルをブラウザにストリーミングしようとします。また、ダウンロードを強制するときにブラウザーがコンテンツタイプ(つまりオクテットストリーム)を認識せず、拡張子がない場合でも、Webページ名を使用します。
RichC 2013年

62

ブラウザのPDFプラグインで処理される代わりに、PDFファイルのダウンロードを強制するには:

public ActionResult DownloadPDF()
{
    return File("~/Content/MyFile.pdf", "application/pdf", "MyRenamedFile.pdf");
}

ブラウザーにデフォルトの動作(プラグインまたはダウンロード)を処理させたい場合は、2つのパラメーターを送信します。

public ActionResult DownloadPDF()
{
    return File("~/Content/MyFile.pdf", "application/pdf");
}

3番目のパラメーターを使用して、ブラウザーダイアログでファイルの名前を指定する必要があります。

更新:3番目のパラメーター(ダウンロードファイル名)を渡すContent-Disposition: attachment;と、HTTP応答ヘッダーにCharlino が追加されます。私の解決策はapplication\force-downloadMIMEタイプとして送信することでしたが、これによりダウンロードのファイル名に問題が発生するため、適切なファイル名を送信するには3番目のパラメーターが必要になるため、ダウンロード強制する必要がなくなります


6
技術的にはそれが起こっていることではありません。技術的には、3番目のパラメーターを追加すると、MVCフレームワークがヘッダーを追加content-disposition: attachment; filename=MyRenamedFile.pdfします。これがダウンロードを強制するものです。MIMEタイプをに戻すことをお勧めしますapplication/pdf
Charlino

2
Charlinoに感謝します。3番目のパラメーターがそうしていることに気づかなかったので、ファイル名を変更するだけだと思いました。
guzart

2
回答を更新し、3番目のパラメータContent-Disposition: attachment;と関係を説明するための+1 。
Charlino

7

Razorやコントローラーでも同じことができます。

@{
    //do this on the top most of your View, immediately after `using` statement
    Response.ContentType = "application/pdf";
    Response.AddHeader("Content-Disposition", "attachment; filename=receipt.pdf");
}

またはコントローラーで

public ActionResult Receipt() {
    Response.ContentType = "application/pdf";
    Response.AddHeader("Content-Disposition", "attachment; filename=receipt.pdf");

    return View();
}

私はこれをChromeとIE9で試しました、どちらもpdfファイルをダウンロードしています。

PDFを生成するためにRazorPDFを使用していることを追加する必要があります。ここにそれについてのブログがあります:http : //nyveldt.com/blog/post/Introducing-RazorPDF


4

コントローラのFileメソッドを見てください。これがまさにそのためです。ActionResultではなくFilePathResultを返します。


3

mgnoonan、

これを行うと、FileStreamを返すことができます。

/// <summary>
/// Creates a new Excel spreadsheet based on a template using the NPOI library.
/// The template is changed in memory and a copy of it is sent to
/// the user computer through a file stream.
/// </summary>
/// <returns>Excel report</returns>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult NPOICreate()
{
    try
    {
        // Opening the Excel template...
        FileStream fs =
            new FileStream(Server.MapPath(@"\Content\NPOITemplate.xls"), FileMode.Open, FileAccess.Read);

        // Getting the complete workbook...
        HSSFWorkbook templateWorkbook = new HSSFWorkbook(fs, true);

        // Getting the worksheet by its name...
        HSSFSheet sheet = templateWorkbook.GetSheet("Sheet1");

        // Getting the row... 0 is the first row.
        HSSFRow dataRow = sheet.GetRow(4);

        // Setting the value 77 at row 5 column 1
        dataRow.GetCell(0).SetCellValue(77);

        // Forcing formula recalculation...
        sheet.ForceFormulaRecalculation = true;

        MemoryStream ms = new MemoryStream();

        // Writing the workbook content to the FileStream...
        templateWorkbook.Write(ms);

        TempData["Message"] = "Excel report created successfully!";

        // Sending the server processed data back to the user computer...
        return File(ms.ToArray(), "application/vnd.ms-excel", "NPOINewFile.xls");
    }
    catch(Exception ex)
    {
        TempData["Message"] = "Oops! Something went wrong.";

        return RedirectToAction("NPOI");
    }
}

1

標準のアクション結果FileContentResultまたはFileStreamResultを使用してファイルをダウンロードできますが、再利用性のために、カスタムアクション結果を作成することが最善の解決策になる場合があります。

例として、データをその場でExcelファイルにエクスポートしてダウンロードするためのカスタムアクション結果を作成してみましょう。

ExcelResultクラスは、抽象ActionResultクラスを継承し、ExecuteResultメソッドをオーバーライドします。

IEnumerableオブジェクトからDataTableを作成するためにFastMemberパッケージを使用し、DataTableからExcelファイルを作成するためにClosedXMLパッケージを使用しています。

public class ExcelResult<T> : ActionResult
{
    private DataTable dataTable;
    private string fileName;

    public ExcelResult(IEnumerable<T> data, string filename, string[] columns)
    {
        this.dataTable = new DataTable();
        using (var reader = ObjectReader.Create(data, columns))
        {
            dataTable.Load(reader);
        }
        this.fileName = filename;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context != null)
        {
            var response = context.HttpContext.Response;
            response.Clear();
            response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            response.AddHeader("content-disposition", string.Format(@"attachment;filename=""{0}""", fileName));
            using (XLWorkbook wb = new XLWorkbook())
            {
                wb.Worksheets.Add(dataTable, "Sheet1");
                using (MemoryStream stream = new MemoryStream())
                {
                    wb.SaveAs(stream);
                    response.BinaryWrite(stream.ToArray());
                }
            }
        }
    }
}

コントローラで、次のようにカスタムExcelResultアクションの結果を使用します

[HttpGet]
public async Task<ExcelResult<MyViewModel>> ExportToExcel()
{
    var model = new Models.MyDataModel();
    var items = await model.GetItems();
    string[] columns = new string[] { "Column1", "Column2", "Column3" };
    string filename = "mydata.xlsx";
    return new ExcelResult<MyViewModel>(items, filename, columns);
}

HttpGetを使用してファイルをダウンロードしているので、モデルと空のレイアウトのない空のビューを作成します。

その場で作成されたファイルをダウンロードするためのカスタムアクション結果に関するブログ投稿:

https://acanozturk.blogspot.com/2019/03/custom-actionresult-for-files-in-aspnet.html


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.