What is a FileResult?
A file download takes place when a handler returns an instance of FileResult.
A FileResult
is a type of ActionResult
. Hence the following are equivalent:
// return a FileResult public async Task<FileResult> OnGetDownloadFileAsync(int? id) { return File(.......); } // the above is same as // or ActionResult public async Task<IActionResult> OnGetDownloadFileAsync(int? id) { return File(.......); }
Video Explanation
Please watch the following youtube video:
How is a FileResult created? (Method 1)
If you have a byte array use this code. The function takes three arguments - first is the byte array, second is the mime type and the name of the downloaded file.
public async Task<FileResult> OnGetDownloadFileAsync(int? id) { // obtain bytes of the file // from database or by directly // reading the files byte[] fileBytes = . . . ; return File( fileBytes, /*byte []*/ "application/pdf", /*mime type*/ "fileName.pdf" /*name of the file*/ ); }
How is a FileResult created? (Method 2)
If you have a path relative to wwwroot
then use this code to directly serve the file. The function also takes three arguments - first is the relative path, second is the mime type and the name of the downloaded file.
public async Task<FileResult> OnGetDownloadFileAsync(int? id) { // relative path to wwwroot String fileBytes = "~/folder/myfile.pdf" ; return File( fileBytes, /*string*/ "application/pdf", /*mime type*/ "fileName.pdf" /*name of the file*/ ); }
Step 1 of 3 - Create a Model
NOTE: it is assumed that database set up is already doneWrite a model class to hold the bytes of the uploaded file.
using Microsoft.EntityFrameworkCore; using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics.CodeAnalysis; namespace DBaseCon { public class UploadedFile { // auto-increment Primary ID key [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } // this column is for a file BLOB public byte[] FileBytes { get; set; } } // -------- DbContext ---------- // // dbcontext DAL - THERE IS ONE DbContext per database public class ProjectContext : DbContext { public ProjectContext(DbContextOptions options) : base(options) { // EnsureCreated is meant for testing or creating // a blank database. this code should be avoided // at this place. use some alternate program to // create/migrate your database. this code has been // used here only for tutorial purposes Database.EnsureCreated(); } // this function is used to specify FK relations, Indexes, // and [optionally] the name of your database tables // corresponding to each model protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // name of the table in your database modelBuilder.Entity<UploadedFile>().ToTable("product"); } // MUST be PLURAL public DbSet<UploadedFile> Files { get; set; } } }
Step 2 of 3 - The backing class
The following code sends an IList
of UploadedFile
. This list is used to display a table of uploaded files with download links.
using DBaseCon; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; namespace Modular.Pages { public class IndexModel : PageModel { private readonly ProjectContext _ctx; // dependency injection of the ProductContext public IndexModel(ProjectContext ctx) { _ctx = ctx; } // handy tool for validation error [TempData] public string Message { get; set; } // onpost handler public async Task<IActionResult> OnPostAsync(List<IFormFile> mylist) { foreach (IFormFile myfile in mylist) { if ( (null == myfile) || (myfile.Length > 1024 * 1000) || (!"application/pdf".Equals(myfile.ContentType)) ) { Message = "File must be pdf type and less than 1 MB"; } else { var uploadedFile = new UploadedFile(); using (MemoryStream ms = new MemoryStream()) { // copy the file to memorystream await myfile.CopyToAsync(ms); // set the byte array uploadedFile.FileBytes = ms.ToArray(); } _ctx.Files.Add(uploadedFile); await _ctx.SaveChangesAsync(); } } return RedirectToPage(); } // list of uploads public IList<UploadedFile> AllUploads { get; set; } // onget handler get the list of // already uploaded files in the database public async void OnGetAsync() { AllUploads = await _ctx.Files.ToListAsync(); } // download handler public async Task<FileResult> OnGetDownloadFileAsync(int? id) { var uploadedFile = await _ctx.Files.FindAsync(id); return File ( uploadedFile.FileBytes, "application/pdf", "fileName.pdf" ); } } }
Step 3 of 3 - The razor markup
Following is the markup.
@page "{handler?}" @model Modular.Pages.IndexModel @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers <h1>Upload a PDF file < 1 MB</h1> @if (TempData.ContainsKey("Message")) { <small style="color:red"> @TempData["Message"] </small> } <form method="post" enctype="multipart/form-data"> <input type="file" name="mylist" required multiple accept="application/pdf, image/png" /> <p /> <input type="submit" value="Upload!" /> </form> @if (Model.AllUploads.Count > 0) { <h2>Download files</h2> <table border="1"> <tr> <th> File Size (bytes) </th> <th> Download </th> </tr> @foreach (var uploadedFile in Model.AllUploads) { <tr> <td> @uploadedFile.FileBytes.Length </td> <td> <a asp-page-handler="DownloadFile" asp-route-id="@uploadedFile.ID">download</a> </td> </tr> } </table> }
This Blog Post/Article "(C# ASP.NET Core) Downloading file(s) and FileResult" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.