web-dev-qa-db-fra.com

Comment puis-je présenter un fichier à télécharger à partir d'un contrôleur MVC?

Dans WebForms, j'aurais normalement un code comme celui-ci pour permettre au navigateur de présenter une fenêtre contextuelle "Télécharger le fichier" avec un type de fichier arbitraire, tel qu'un PDF, et un nom de fichier:

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()

Comment accomplir la même tâche dans ASP.NET MVC?

107
mgnoonan

Renvoyez un FileResult ou FileStreamResult à partir de votre action, selon que le fichier existe ou que vous le créez à la volée.

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

Pour forcer le téléchargement d'un fichier PDF, au lieu d'être traité par le plugin PDF du navigateur) du navigateur:

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

Si vous voulez laisser le navigateur gérer par son comportement par défaut (plugin ou téléchargement), envoyez simplement deux paramètres.

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

Vous devrez utiliser le troisième paramètre pour spécifier un nom pour le fichier dans la boîte de dialogue du navigateur.

UPDATE: Charlino a raison, en passant le troisième paramètre (nom du fichier téléchargé) Content-Disposition: attachment; est ajouté à l’en-tête de réponse HTTP. Ma solution était d'envoyer application\force-download en tant que type mime, mais cela crée un problème avec le nom de fichier du téléchargement, le troisième paramètre est donc nécessaire pour envoyer un bon nom de fichier, éliminant ainsi le besoin de forcer un téléchargement.

62
guzart

Vous pouvez faire la même chose dans Razor ou dans le contrôleur, comme si ..

@{
    //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");
}

Ou dans le contrôleur ..

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

    return View();
}

J'ai essayé ceci dans Chrome et IE9, les deux téléchargent le fichier pdf.

Je devrais probablement ajouter que j'utilise RazorPDF pour générer mes PDF. Voici un blog à ce sujet: http://nyveldt.com/blog/post/Introducing-RazorPDF

7
Rosdi Kasim

Vous devriez regarder la méthode File du contrôleur. C'est exactement ce que c'est. Il retourne un FilePathResult au lieu d'un ActionResult.

4
Martin Peck

mgnoonan,

Vous pouvez le faire pour retourner un 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");
    }
}
3
Leniel Maccaferri

Bien que les résultats des actions standard puissent être utilisés pour le téléchargement de fichiers, FileContentResult ou FileStreamResult peuvent être utilisés, mais la création d'une action personnalisée peut être la meilleure solution.

À titre d'exemple, créons un résultat d'action personnalisé pour exporter des données vers des fichiers Excel à la volée en vue de leur téléchargement.

La classe ExcelResult hérite de la classe abstraite ActionResult et remplace la méthode ExecuteResult.

Nous utilisons le package FastMember pour créer DataTable à partir d'un objet IEnumerable et le package ClosedXML pour créer un fichier Excel à partir de DataTable.

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());
                }
            }
        }
    }
}

Dans le contrôleur, utilisez le résultat de l'action ExcelResult personnalisé comme suit

[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);
}

Comme nous téléchargeons le fichier à l’aide de HttpGet, créez une vue vide sans modèle et une présentation vide.

Article de blog sur le résultat de l'action personnalisée pour le téléchargement de fichiers créés à la volée:

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

0
Ahmetcan Ozturk