ฉันจะนำเสนอไฟล์สำหรับดาวน์โหลดจากตัวควบคุม MVC ได้อย่างไร


109

ใน WebForms ปกติฉันจะมีรหัสแบบนี้เพื่อให้เบราว์เซอร์แสดงป๊อปอัป "ดาวน์โหลดไฟล์" พร้อมกับประเภทไฟล์ที่กำหนดเองเช่น 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 บรรทัดสามารถทำได้ในบรรทัดเดียว ง่ายกว่ามาก!
Jon Kruger

ขอบคุณ tvanfosson ฉันค้นหาวิธีที่ดีที่สุดในการทำสิ่งนี้และมันก็เยี่ยมมาก
Mark Kadlec

1
สิ่งนี้ต้องใช้นามสกุลไฟล์ในชื่อไฟล์มิฉะนั้นจะเพิกเฉยต่อชื่อไฟล์และประเภทเนื้อหาทั้งหมดและพยายามสตรีมไฟล์ไปยังเบราว์เซอร์ นอกจากนี้ยังจะใช้ชื่อหน้าเว็บหากเบราว์เซอร์ไม่รู้จักประเภทเนื้อหา (เช่น octet-stream) เมื่อบังคับให้ดาวน์โหลดและจะไม่มีส่วนขยายเลย
RichC

62

หากต้องการบังคับให้ดาวน์โหลดไฟล์ PDF แทนที่จะจัดการโดยปลั๊กอิน PDF ของเบราว์เซอร์:

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

หากคุณต้องการให้เบราว์เซอร์จัดการตามลักษณะการทำงานเริ่มต้น (ปลั๊กอินหรือดาวน์โหลด) เพียงแค่ส่งพารามิเตอร์สองตัว

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

คุณจะต้องใช้พารามิเตอร์ที่สามเพื่อระบุชื่อไฟล์ในกล่องโต้ตอบเบราว์เซอร์

อัปเดต: Charlino ถูกต้องเมื่อส่งผ่านพารามิเตอร์ที่สาม (ชื่อไฟล์ดาวน์โหลด) Content-Disposition: attachment;จะถูกเพิ่มไปยังส่วนหัวการตอบสนอง Http วิธีการแก้ปัญหาของฉันคือการส่งapplication\force-downloadเป็นชนิด mime แต่นี้สร้างปัญหากับชื่อไฟล์ที่ดาวน์โหลดไว้เพื่อพารามิเตอร์ที่สามจะต้องส่งชื่อไฟล์ที่ดีจึงไม่จำเป็นต้องบังคับให้ดาวน์โหลด


6
ในทางเทคนิคนั่นไม่ใช่สิ่งที่เกิดขึ้น ในทางเทคนิคเมื่อคุณเพิ่มพารามิเตอร์ที่สามเฟรมเวิร์ก MVC จะเพิ่มส่วนหัวcontent-disposition: attachment; filename=MyRenamedFile.pdfซึ่งเป็นสิ่งที่บังคับให้ดาวน์โหลด application/pdfฉันขอแนะนำให้คุณใส่ชนิดไมม์กลับไป
Charlino

2
ขอบคุณชาร์ลิโนฉันไม่รู้ว่าพารามิเตอร์ตัวที่สามกำลังทำเช่นนั้นฉันคิดว่ามันเป็นเพียงการเปลี่ยนชื่อไฟล์
guzart

2
+1 สำหรับอัปเดตคำตอบของคุณและอธิบายพารามิเตอร์ที่สาม + Content-Disposition: attachment;ความสัมพันธ์
Charlino

7

คุณสามารถทำได้เช่นเดียวกันใน Razor หรือใน Controller เช่นนั้น ..

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

หรือใน Controller ..

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

    return View();
}

ฉันลองสิ่งนี้ใน Chrome และ IE9 ทั้งคู่กำลังดาวน์โหลดไฟล์ pdf

ฉันควรเพิ่มฉันใช้RazorPDFเพื่อสร้าง PDF ของฉัน นี่คือบล็อกเกี่ยวกับเรื่องนี้: http://nyveldt.com/blog/post/Introducing-RazorPDF


4

คุณควรดูวิธีการไฟล์ของคอนโทรลเลอร์ นี่คือสิ่งที่แน่นอนสำหรับ ส่งคืน FilePathResult แทน ActionResult


3

มกนูนัน

คุณสามารถทำได้เพื่อส่งคืน 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

เราใช้แพ็คเกจ FastMember ในการสร้าง DataTable จากอ็อบเจ็กต์ IEnumerable และแพ็คเกจ ClosedXML สำหรับสร้างไฟล์ Excel จาก 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());
                }
            }
        }
    }
}

ใน Controller ใช้ผลลัพธ์การดำเนินการ 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 ให้สร้าง View ว่างโดยไม่มีโมเดลและเลย์เอาท์เปล่า

โพสต์บล็อกเกี่ยวกับผลการดำเนินการที่กำหนดเองสำหรับการดาวน์โหลดไฟล์ที่สร้างขึ้นทันที:

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


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.