Как я могу представить файл для загрузки с контроллера 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

Верните a FileResultили FileStreamResultиз вашего действия, в зависимости от того, существует ли файл, или вы создаете его на лету.

public ActionResult GetPdf(string filename)
{
    return File(filename, "application/pdf", Server.UrlEncode(filename));
}
tvanfosson
источник
14
Это отличный пример того, почему ASP.NET MVC великолепен. То, что вам раньше приходилось делать в 9 строках запутанного кода, можно сделать в одной строке. Намного проще!
Джон Крюгер,
Спасибо tvanfosson, я искал лучшее решение для этого, и это здорово.
Марк Кадлец
1
Это требует расширения файла в имени файла, иначе он полностью проигнорирует имя файла и тип содержимого и просто попытается передать файл в браузер. Он также будет просто использовать имя веб-страницы, если браузер не распознает тип содержимого (например, октет-поток) при принудительной загрузке, и у него вообще не будет расширения.
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");
}

Вам нужно будет использовать третий параметр, чтобы указать имя файла в диалоговом окне браузера.

ОБНОВЛЕНИЕ: Чарлино прав, при передаче третьего параметра (имя файла загрузки) Content-Disposition: attachment;добавляется в заголовок ответа Http. Мое решение состояло в том, чтобы отправить application\force-downloadкак mime-тип, но это создает проблему с именем файла для загрузки, поэтому третий параметр требуется для отправки хорошего имени файла, что устраняет необходимость принудительной загрузки .

гузарт
источник
6
Технически это не то, что происходит. Технически, когда вы добавляете третий параметр, инфраструктура MVC добавляет заголовок content-disposition: attachment; filename=MyRenamedFile.pdf- это то, что вызывает загрузку. Я бы посоветовал вам вернуть тип MIME application/pdf.
Чарлино
2
Спасибо, Чарлино, я не понял, что это делает третий параметр, я думал, что это просто изменение имени файла.
guzart 01
2
+1 за обновление вашего ответа и объяснение третьего параметра + Content-Disposition: attachment;отношения.
Чарлино
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.

Я, наверное, должен добавить, что использую RazorPDF для создания своих PDF-файлов. Вот блог об этом: http://nyveldt.com/blog/post/Introduction-RazorPDF

Росди Касым
источник
4

Вы должны посмотреть на метод File контроллера. Это именно то, для чего он нужен. Он возвращает 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());
                }
            }
        }
    }
}

В контроллере используйте результат настраиваемого действия 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

Ахметкан Озтюрк
источник
-4

Используйте тип файла .ashx и используйте тот же код

0100110010101
источник