Bir MVC kontrol cihazından indirilecek bir dosyayı nasıl sunabilirim?


109

WebForms'ta, normalde tarayıcının PDF gibi rasgele bir dosya türü ve bir dosya adı içeren bir "Dosyayı İndir" açılır penceresi sunmasına izin vermek için böyle bir koda sahip olurdum:

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'de aynı görevi nasıl gerçekleştiririm?

Yanıtlar:


181

Dosyanın var olmasına veya anında oluşturmanıza bağlı olarak eyleminizden bir FileResultveya FileStreamResultdöndürün.

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

14
Bu, ASP.NET MVC'nin neden harika olduğuna dair harika bir örnektir. Daha önce 9 satır kafa karıştırıcı görünen kodda yapmanız gerekenler tek satırda yapılabilir. Çok daha kolay!
Jon Kruger

Teşekkürler tvanfosson, bunu yapmak için en iyi çözümü aradım ve bu harika.
Mark Kadlec

1
Bu, dosya adı üzerinde bir dosya uzantısı gerektirir, aksi takdirde dosya adını ve içerik türünü tamamen yok sayar ve sadece dosyayı tarayıcıya aktarmaya çalışır. Ayrıca, tarayıcı indirmeyi zorladığında içerik türünü (yani sekizli akışı) tanımazsa ve hiçbir uzantıya sahip olmazsa, yalnızca web sayfası adını kullanır.
RichC

62

Tarayıcının PDF eklentisi tarafından işlenmek yerine bir PDF dosyasının indirilmesini zorlamak için:

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

Tarayıcının varsayılan davranışıyla (eklenti veya indirme) işlemesine izin vermek istiyorsanız, yalnızca iki parametre gönderin.

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

Tarayıcı iletişim kutusunda dosya için bir ad belirtmek için üçüncü parametreyi kullanmanız gerekir.

GÜNCELLEME: Charlino haklıdır, üçüncü parametre (indirme dosya adı) geçerken Content-Disposition: attachment;Http Yanıt Başlığına eklenir. Benim çözümüm application\force-downloadmime türü olarak göndermekti , ancak bu, indirmenin dosya adıyla ilgili bir sorun yaratır, bu nedenle üçüncü parametrenin iyi bir dosya adı göndermek için gerekli olması, dolayısıyla indirmeye zorlama ihtiyacını ortadan kaldırır .


6
Teknik olarak olan bu değil. Teknik olarak üçüncü parametreyi eklediğinizde, MVC çerçevesi başlığı ekler content-disposition: attachment; filename=MyRenamedFile.pdf- indirmeyi zorlayan şey budur. MIME türünü geri koymanızı öneririm application/pdf.
Charlino

2
Teşekkürler Charlino, üçüncü parametrenin bunu yaptığını fark etmedim, sadece dosya adını değiştirmek olduğunu düşündüm.
guzart

2
Cevabınızı güncellemek ve üçüncü parametre + Content-Disposition: attachment;ilişkiyi açıklamak için +1 .
Charlino

7

Aynısını Razor'da veya Controller'da da yapabilirsiniz, bunun gibi ..

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

Veya Kontrolörde ..

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

    return View();
}

Bunu Chrome ve IE9'da denedim, ikisi de pdf dosyasını indiriyor.

Muhtemelen PDF'lerimi oluşturmak için RazorPDF kullanıyorum eklemeliyim . İşte bununla ilgili bir blog: http://nyveldt.com/blog/post/Introducing-RazorPDF


4

Denetleyicinin Dosya yöntemine bakmalısınız. Bu tam olarak bunun için. ActionResult yerine FilePathResult döndürür.


3

mgnoonan,

Bir FileStream döndürmek için bunu yapabilirsiniz:

/// <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 veya FileStreamResult standart eylem sonuçları dosyaları indirmek için kullanılabilse de, yeniden kullanılabilirlik için özel bir eylem sonucu oluşturmak en iyi çözüm olabilir.

Örnek olarak, verileri indirmek için anında Excel dosyalarına aktarmak için özel bir eylem sonucu oluşturalım.

ExcelResult sınıfı, soyut ActionResult sınıfını miras alır ve ExecuteResult yöntemini geçersiz kılar.

IEnumerable nesnesinden DataTable oluşturmak için FastMember paketi ve DataTable'dan Excel dosyası oluşturmak için ClosedXML paketi kullanıyoruz.

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

Denetleyicide özel ExcelResult eylem sonucunu aşağıdaki gibi kullanın

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

Dosyayı HttpGet kullanarak indirdiğimiz için, model ve boş layout olmadan boş bir View oluşturun.

Anında oluşturulan dosyaları indirmek için özel eylem sonucu hakkında blog yayını:

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


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.