Làm cách nào để trình bày tệp để tải xuống từ bộ điều khiển MVC?


109

Trong WebForms, tôi thường có mã như thế này để cho phép trình duyệt hiển thị cửa sổ bật lên "Tệp tải xuống" với loại tệp tùy ý, như PDF và tên tệp:

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

Làm cách nào để hoàn thành tác vụ tương tự trong ASP.NET MVC?

Câu trả lời:


181

Trả lại một FileResulthoặc FileStreamResulttừ hành động của bạn, tùy thuộc vào việc tệp có tồn tại hay bạn tạo nó nhanh chóng.

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

14
Đây là một ví dụ tuyệt vời về lý do tại sao ASP.NET MVC tuyệt vời. Những gì trước đây bạn phải làm trong 9 dòng mã trông khó hiểu có thể được thực hiện trong một dòng. Vì vậy, dễ dàng hơn nhiều!
Jon Kruger

Cảm ơn tvanfosson, tôi đã tìm kiếm giải pháp tốt nhất để thực hiện việc này và điều này thật tuyệt.
Mark Kadlec,

1
Điều này yêu cầu một phần mở rộng tệp trên tên tệp hoặc nếu không nó sẽ hoàn toàn bỏ qua tên tệp và loại nội dung và chỉ cố truyền tệp tới trình duyệt. Nó cũng sẽ chỉ sử dụng tên trang web nếu trình duyệt không nhận ra loại nội dung (tức là octet-stream) khi nó buộc tải xuống và nó sẽ không có phần mở rộng nào cả.
RichC

62

Để buộc tải xuống tệp PDF, thay vì được xử lý bởi plugin PDF của trình duyệt:

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

Nếu bạn muốn để trình duyệt xử lý theo hành vi mặc định của nó (plugin hoặc tải xuống), chỉ cần gửi hai tham số.

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

Bạn sẽ cần sử dụng tham số thứ ba để chỉ định tên cho tệp trên hộp thoại trình duyệt.

CẬP NHẬT: Charlino đúng, khi truyền tham số thứ ba (tên tệp tải xuống) Content-Disposition: attachment;sẽ được thêm vào Tiêu đề phản hồi Http. Giải pháp của tôi là gửi application\force-downloaddưới dạng mime-type, nhưng điều này tạo ra sự cố với tên tệp của tệp tải xuống vì vậy tham số thứ ba là bắt buộc để gửi một tên tệp tốt, do đó loại bỏ nhu cầu buộc tải xuống .


6
Về mặt kỹ thuật, đó không phải là những gì đang xảy ra. Về mặt kỹ thuật khi bạn thêm tham số thứ ba, khuôn khổ MVC sẽ thêm tiêu đề content-disposition: attachment; filename=MyRenamedFile.pdf- đây là những gì buộc tải xuống. Tôi khuyên bạn nên đặt lại kiểu MIME application/pdf.
Charlino

2
Cảm ơn Charlino, tôi không nhận ra thông số thứ ba đang làm điều đó, tôi nghĩ đó chỉ là thay đổi tên tệp.
guzart

2
+1 để cập nhật câu trả lời của bạn và giải thích Content-Disposition: attachment;mối quan hệ + tham số thứ ba .
Charlino

7

Bạn có thể làm tương tự trong Razor hoặc trong Bộ điều khiển, như vậy ..

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

Hoặc trong Bộ điều khiển ..

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

    return View();
}

Tôi đã thử điều này trong Chrome và IE9, cả hai đều đang tải xuống tệp pdf.

Tôi có lẽ nên thêm Tôi đang sử dụng RazorPDF để tạo các tệp PDF của mình. Đây là một blog về nó: http://nyveldt.com/blog/post/Introductioning-RazorPDF


4

Bạn nên xem phương thức Tệp của Bộ điều khiển. Đây chính xác là những gì nó dành cho. Nó trả về một FilePathResult thay vì một ActionResult.


3

mgnoonan,

Bạn có thể thực hiện việc này để trả về một 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

Mặc dù kết quả hành động tiêu chuẩn FileContentResult hoặc FileStreamResult có thể được sử dụng để tải xuống tệp, để có thể sử dụng lại, tạo kết quả hành động tùy chỉnh có thể là giải pháp tốt nhất.

Ví dụ, hãy tạo một kết quả hành động tùy chỉnh để xuất dữ liệu sang tệp Excel ngay lập tức để tải xuống.

Lớp ExcelResult kế thừa lớp ActionResult trừu tượng và ghi đè phương thức ExecuteResult.

Chúng tôi đang sử dụng gói FastMember để tạo DataTable từ đối tượng IEnumerable và gói ClosedXML để tạo tệp Excel từ 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());
                }
            }
        }
    }
}

Trong Bộ điều khiển, sử dụng kết quả hành động ExcelResult tùy chỉnh như sau

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

Vì chúng tôi đang tải xuống tệp bằng HttpGet, hãy tạo một Chế độ xem trống không có mô hình và bố cục trống.

Bài đăng trên blog về kết quả hành động tùy chỉnh để tải xuống các tệp được tạo nhanh chóng:

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


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.